15

I have started creating a test application in .Net, that uses EventStore by Greg Young as the backing store for CQRS/ES.

In order to make it easy to load up a full aggregate, I save to a stream with a name of "agg-123". For example for a product aggregate with an id of 553, there would be a stream named "product-553". And then same for an "Order" aggregate, the stream would be named "order-123".

From the rehydrating and saving of events this works well.

I am now attempting to create a listener that will listen to certain streams to then populate a query database. The subscribe methods that I see can only seem to subscribe to "order-123", or "all". I can't see how I would subscribe to "product-" or "order-", or both.

I imagine that either

  • I have missed the point with stream names, and have named them wrong
  • Have missed a way to choose it, something like "product-*"
  • It is expected to subscribe to "all" and filter out what you aren't interested in, although this gives the problem that it also sends all of the "stats" events

Anyone any advice?

mat-mcloughlin
  • 6,492
  • 12
  • 45
  • 62
eyeballpaul
  • 1,725
  • 2
  • 25
  • 39
  • Hi Eyeball, if possible - can you check out my question here: https://stackoverflow.com/questions/35828118/subscribe-to-stream-event-never-appear-in-subscription-client - seems like I have somewhat the "problem". – janhartmann Mar 06 '16 at 14:45

3 Answers3

12

In your projections you can use fromCategory(). EventStore categorises each stream by the name of the stream up to the first '-'. So your 'order-123' and 'order-456' streams are both in the 'order' category.

So you can do something like:

fromCategory('order')
  .whenAny(function(s,e) {
    linkTo('orders',e);
  });

This will project all order related events from all order aggregates into a single 'orders' stream that you can subscribe.

You'll need to ensure that the category projections are running. (It's been a while since I've used EventStore, it was back when projections were in beta and weren't enabled by default, not sure if things are the same in the latest versions)

Matt
  • 2,984
  • 1
  • 24
  • 31
5

From The cost of creating a stream article:

Generally when people are wanting only a few streams its because they want to read things out in a certain way for a particular type of reader. This can be done in other ways. Internally the Event Store is essentialy a topic based pub/sub. What you can do is repartition your streams utilizing projections to help provide for a specific reader. As an example let's say that a reader was interested in all the InventoryItemCreated and InventoryItemDeactivated events but was not interested in all the other events in the system. Supporting this stream when we have the events in many millions of streams can still be done.

To do this we will create a projection to reindex the streams.

So the idea is that you could create two projections to emit events to some products and orders streams.

In the same article the system bytype projection is mentioned, which creates a stream for each event type with the name $et-{typename}. This could also prove useful in your case.

For instance, if you are only interested in observing the creation of the aggregates, you could subscribe to the corresponding streams. Assuming that you have some productCreated and orderCreated events, you would simply subscribe to the $et-productCreated and $et-orderCreated events. Upon receiving events from these streams, you could also subscribe to the individual streams (eg. product-553 and order-123) simply by consuming the Id from the *Created events.

(Note: projections are disabled by default and once enabled you may need to manually start the bytype projection.)

Community
  • 1
  • 1
jnovo
  • 5,659
  • 2
  • 38
  • 56
  • In this scenario, I would be leaning more to the full projection than by type. I don't see how I can do what I want by projection though. You can create one with "fromstream('product-123')", but I don't see how I can project all "product-xxx" into one stream without manually adding all ID's which I cannot do. – eyeballpaul Jan 12 '16 at 18:14
  • I can't see a way using a normal projection to do this, perhaps I need to use your second idea, by choosing event types? I would basically be wanting EVERY event type for "product" and "order" aggregate. It does mean I need to manually change the projection every time I create a new event type though. – eyeballpaul Jan 12 '16 at 18:23
  • Other approaches: create `products` and `orders` streams and then projections to emit to Id-specific streams (eg. `product-553`storing all the events for the product with Id 553), specify the aggregate type in the event metadata perform filtering in the projections. – jnovo Jan 12 '16 at 19:42
  • @jnovo, could you have a look at my question here: https://stackoverflow.com/questions/52447808/handle-all-events-for-an-aggregate ? It is similar to this question. +1. – w0051977 Sep 21 '18 at 20:03
5

You need to check if projections are running, since this feature is disabled by default at the time of writing this post since the Projections feature is still in beta.

There is a special type projection called category projection that is what you probably need. If you have streams that are named following the name-id pattern, for example product-123, product-234, you can subscribe to $ce-product stream to receive all events that are saved to these streams.

Alexey Zimarev
  • 17,944
  • 2
  • 55
  • 83
  • Thanks for your answer, but Matt already provided this answer, and I've accepted it. – eyeballpaul Feb 22 '16 at 14:03
  • 3
    That's ok but I have not seen any one here mentioning the $ce- stream. This is the GES default projection that exists by default. I see Matt mentioned "category projection" and of course this is exactly the same one, however, you don't have to create a separate aggregation stream, it is available under the name I mentioned out of the box. – Alexey Zimarev Feb 22 '16 at 14:07
  • 1
    No problem. The more information for folk in the future the better. – eyeballpaul Feb 22 '16 at 14:07
  • 2
    I used the fromCategory().linkto myself until I realized this is completely unnecessary. – Alexey Zimarev Feb 22 '16 at 14:09