Event sourced process managers
In the comments to one of my previous posts Jarek pointed out that the set of concepts I use (based on excellent work by Rinat Abdullin) reminds him, rightfully, of Boundary-Control-Entity pattern. A much better summary of the pattern (and related patterns) than I would be ever capable of writing has already been provided by Uncle Bob. TL;DR: put technology-agnostic things in the centre and technology-aware on the outside, use Dependency Inversion Principle to wire things up.
What I want to clear is the place of Controls component in my model. Intuitively is seems like a Command Handler plays this role but that turns out to be impossible for a couple of reasons. A Command Handler is only responsible for translating commands (messages) to method calls on Aggregates. I can’t even touch more than one aggregate when processing a command (due to scalability requirements). Last but not least, it is stateless. So if not Command Handler than what?
Meet Process Manager (a.k.a. Saga). A Process Manager coordinates long running processes by means of message exchange. In some technology stacks (e.g. NServiceBus) Sagas are a built-in mechanism that programmers can use out-of-the-box. In my custom-built stack, however, Saga is just another Aggregate. There is absolutely no difference in technology. The difference is in behaviour. While normal Aggregates are usually passive (they execute the business logic and emit events for state changes), a Process Manager is active in a sense that it usually expects a response for the events it emits. Process Manager-as-an-aggregate needs to accompanied by a bunch of helper components to perform it’s job, namely:
- Receptor that reacts on outside events and turns them into commands understood by a Process Manager
- Command Handler that translates those commands to method calls
- Gateway that translates Process Manager’s state changes (events) into commands sent to other Aggregates
And there we arrived at Jarek’s quesion: why do we need Gateways at all? We could as well use a Receptor on receiving Aggregate’s side, right? Their raison d’être is subtle. In case of a Process Manager, the responsibility for generating a command (an order if you will) is on PM’s side and the receiving Aggregate can’t ignore it. If we used a Receptor (logically belonging to this other Aggregate), it would mean that PM’s event can be safely ignored.