Event Dispatcher Or everyone subscribes?

Hi, new, probably dumb question.
I’ve been reading a lot of the dialog on here about event dispatchers. It seems it starts off with some folks writing event dispatchers, then some or all of that functionality is incorporated into catchupsubscriptions. Now I’m not sure what the best practice is.

Should I have a single event dispatcher catch all events then publish them on a bus? Or have a single event dispatcher catch all events then send them to respective handlers? or should I have each handler subscribe to ES and handle it themselves.

I’m sure part of the consideration is load and through put etc. Also it seems like the event dispatcher code I’ve seen is pretty simple, in that it handles start, stop, restart, deserialize and then dispatch. Most of this would have to happen even if I have each handler subscribe themselves. Still it’s not so much code that it couldn’t be repeated or abstracted.

Any thoughts are welcome.

R

The more I think about it the more it seems that having each Handler implement the “dispatcher” code for it’s self ( most likely consuming a service or baseclass or something ) is the right way to go, this way we can track the lastpositionprocessed in a more meaningful manner. If I do it at a dispatcher level, then it only says “this has been dispatched” which is fine I guess if you are putting it on a guaranteed at least once bus. But I’d rather not. If I persist location at the event handler level then each handler tracks it’s own “state”
still if anyone wants to confirm or dis-confirm, it would certainly boost my confidence in this area.

Thanks

R

Bandwidth-wise, it’s probably more efficient to have 1 dispatcher per service. I.e. 1 subscription + dispatcher for updating all read models. Otherwise, it is pretty likely that you will be subscribing to the same message multiple times across different read model updaters. E.g. a list/search view, and a details view.

You can still have each view updater keep track of its own position. When the service restarts, the dispatcher asks everybody the last position they processed and then restarts from the lowest one. Updaters ignore messages which they have already seen (message position lower than current position). I’d tend to put this behavior in a separate class (a position tracker?) that is composed with the updater.

P.S. I don’t use a bus. I just use an ActionBlock (from the .NET TPL DataFlow library) as a queue + worker. I don’t know why you would need a bus for this, since you can resubscribe at any time from where you left off.

Err, I use an ActionBlock per updater, and another one for the dispatcher.

OTOH, wouldn’t the repeat requests get served from local cache/disk?

I suppose if you were using the Atom feed they would. I’m not.

Oh. The tcp link bypasses the ability to cache the individual events. I guess you’d have to roll your own there, but then, you may as well just do what you described with the single dispatcher per service.

I’m trying to wrap my head around the ActionBlock as I’m not familiar with it. I guess you pass your updaters to the ActionBlock and it spawns a thread to execute it in parallel?
R

At base, ActionBlock is like a typical queue + worker loop (only one item at a time is processed), except it is a packaged component.

I give each view a queue/worker so they can process their own messages in order but operate independently of one another.

I give one to the dispatcher because I don’t want my subscription to get dropped for processing too slow, and for now I am deserializing from ResolvedEvent to my domain event in the dispatcher before sending to view updaters.

Interesting. I’ll have to read more on the ActionBlock implementation, but at a high level I think I get it.
you get an event, deserialize it and then hand it off to a view in an async manner so you don’t time out, then the view also process it in an async manner. I’ve got it mixed up somehow, but I think I understand.

And you’re views are generically typed by the domain event so you can just instantiate with the event as a strategy? Or do you have some other strategy. Or is there not a strat required. I would think there is given that you have one dispatcher for all views.

R

I’m desperately hoping for c# to get a pattern match in the next version. (It’s in the draft spec for 6.0.) In the mean time, I’m using interfaces on the updater to declare what message types are handled. During initialization, the dispatcher is using reflection to find them (and in the darkness bind them).

It’s not my preferred way to use reflection magic, but I had the following considerations when wrestling with this decision:

  • view updates are low-value code that I want to be as frictionless as possible

  • I don’t need the flexibility of manual handler registration, so eliminate that friction

For the command handler side, I do use hard-coded manual command registration because I need the flexibility to inject dependencies (e.g. credit card service) and add steps to the handling pipeline.

Yea, I was just thinking about this in the boxing gym while I was getting my face pummeled by a 20 year old black kid. I realized that you could mark all your view handlers with grouping interfaces and inject them into the dispatcher with an ioc container. I was also thinking about what a cool dispatching system you could make like this. Very exciting.
It sounds like you are also using event store to handle your commands. I’ve spoken to someone else who does that and it’s sounding more and more appealing.

R

I am just using event store to log commands and their results at this point. I started with clients posting commands to a stream, and a command listener subscribing to the stream and executing them. However, there are a lot of edge cases to explicitly handle with this method. I had it working, but ultimately I decided that it wasn’t needed since much simpler alternatives exist that are still scalable (should the need arise). And maintaining it going forward was a bit more work for little return.

I also don’t use an ioc container. For commands, I pass the dependency in with the method call. The real service has a wire-up which handles providing dependency objects or dependency factories (e.g. Func) to the method calls. The unit tests just pass in mock services instead. I don’t have any container registration.

I"m kind of new (duh) to the DDD layers or circles or whatever, so I’m not sure how IOC will work with that till I get there. Also, apparently getting my face pummeled didn’t help my thought process cuz what I wrote about the strategy doesn’t really fit. Nonetheless! this is good stuff.
I did wonder about out of band event processing. You spoke about your view updaters drawing off of the dispatcher, but what about other services that need to listen to events? do you have a couple dispatchers, one for say emails, one for view updates, and one for outside BCs?

R

Yes, 1 dispatcher per service, but multiple services are possible. I’m only at one (event-subscribing) service now, but still in the early stages.

Cool, that makes sense.
thanks,

R