13

Does a process manager make use of correlation-ids or aggregate-specific identifies to keep track of the process it is managing?

To put it more clearly with an example, consider Figure 2 on Saga on Sagas:

enter image description here

First of all, Process Manager sending a OrderConfirmed event is wrong right? I (as a process manager) cannot send events, only issue commands. Or am I wrong?

Second, how does the process manager correlate the OrderCreated, SeatsReserved, PaymentReceived events from different aggregates? Is it correlation id that each aggregate honors (and copies over), or is it specific identifiers (e.g. SeatsReserved has an Order Id that refers to the Order Aggregate)?

Finally, if it is the case of correlation ids, who creates them? Is it the client who issues a command, like PlaceOrder(order_id, correlation_id)? Is it the Aggregate that accepts the command like PlaceOrder(order_id) and then emits OrderCreated(order_id, corr_id) event? Or, is it the process manager (in some way) that is responsible for this? Alternatively, perhaps correlation ids have nothing to do with this?

Thanks for any help.

Andriy Drozdyuk
  • 58,435
  • 50
  • 171
  • 272

4 Answers4

8

Highly recommend to get through the original source of the Process Manager design pattern as well as @Vaughn Vernon treatment of the topic in his great book Implementing Domain-Driven Design

In a nutshell, Process Manager deals with multiple processes concurrently via Process instances that it creates when a specific process is initiated. Id of a Process instance is your Correlation Identifier that is part of the payload of every communication (commands/events) in the duration of the process. There is another term for Process instance, that is Process Tracker. The idea is the same.

So in this approach, there is a clear separation of concerns. Every Process Instance is responsible for its own process in terms of its current state, retries, finishing etc. It is a Process Instance that is responsible for emitting ProcessFinished event since it is the "brain".

Another name for Process Manager is Long Running Process. So by definition, it is better be asynchronous.

~ Sergiy

<><

Sergiy Chernets
  • 427
  • 2
  • 5
  • Thanks, I've read both. But I couldn't really understand how correlation identifiers come into play in the aggregate. What you are saying is that correlation identifiers are a _concept_ not an explicit field on events and commands, right? – Andriy Drozdyuk Mar 05 '17 at 08:52
  • 1
    @drozzy correlation identifier is part of the command/event payload - explicit field. And it is the pattern/concept to solve the problem of correlation distributed workflow steps. The nature of the correlation identifier is secondary. The only requirement for it is to be unique. – Sergiy Chernets Mar 05 '17 at 09:30
  • 1
    @drozzy I should mention that correlation identifier is not part of the ddd model rather it is part of the message abstraction that command/event is derivative of. – Sergiy Chernets Mar 05 '17 at 09:39
  • 2
    @SergiyChernets The thing I don't understand is this. When some command/event leads to an action on a completely different aggregat, for instance `CloseOrder()` on an `Order` aggregate. Then the `Order` aggregate will raise an `OrderClosed` event. How does the CorrelationId end up in that event? It seems ugly to have to add a CorrelationId to every method on the aggregate... – Robba Nov 01 '18 at 07:16
6

I regard a Process Manager as a first class citizen in my domain. The Id of the process manager instance would typically act as the correlation Id. Since the process manager is a state machine it can, in reality, publish events. Well, I regard Domain Events as different from System Events. The messaging infrastructure relies on System Events. These are the ones that would carry the correlation id and, yes, it would be copied to the relevant messages but this is something that your infrastructure could do automatically. In my Shuttle.Esb service bus I do just that: copy headers and correlation id.

This is why I also regard the process side of things as almost a type of BC in and of itself. It interacts with the various BCs that form part of the process by issuing command and then responding to the relevant events. But on the infrastructure (endpoint) layer I may want to publish an event that states that a particular process has, say, been abandoned or completed or moved from one step to the next.

edit:

In my view the process manager itself is also an aggregate. All commands and events would come from the message handler / application layer in response to what happens in the domain. Any events that do come from the domain would, by their very nature, be domain events and these are not necessarily appropriate for system to system communication. I guess it would be more appropriate to say "on the integration / application layer" than on the infrastructure (endpoint) layer.

All messages are handled in a message handler. That message handler acts as the integration point and has to interact with the domain either directly or through an application layer. My wording may not have made that apparent :)

Eben Roux
  • 12,983
  • 2
  • 27
  • 48
  • Thanks, could you clarify what you mean by "on the infrastructure layer" wanting to publish an event? If process abandons something, wouldn't it issue a command to relevant aggregate or be picked up by another process manager (e.g. due to timeout)? – Andriy Drozdyuk Mar 05 '17 at 10:17
  • What an interesting view! I never thought of the process as a separate bounded context, with its own concerns. – Andriy Drozdyuk Mar 05 '17 at 19:24
4

Disclaimer: since the example you mentioned uses the "saga" and "process manager" terms interchangeably, I will do that as well. I do know that saga and process manager patterns are different by definition, but in the industry we see that the word saga is being applied by many popular frameworks (starting with NServiceBus).

Looking at this particular example, it would be:

  • Publishing events from the saga: it depends. I would strongly warn you against building any dogmatic approach towards who can do what. You can never know what the next day brings and you shall not just do everything "by the book". If the ProcessManager orchestrates the whole process, it might very well consume the OrderConfirmed event from one aggregate or context, send a command to the next step, wait for it to produce a confirmation event and when the whole process is completed - issue its own event, which is in that cases it sent to the customer via some UI magic. I do not see anything wrong in this approach. There might be a better solution though.

  • Typically, saga id == correlation id and it is either some artificial short living identified, in which case you will indeed need to keep track on the correlation and either rely on your infrastructure to pass it through from commands to events on the command handler side. You will need to generate such id as soon as the saga instance gets created (remember, the saga has the logic and the state, and each running process has its own state). Or you can use some domain object id, like in the case you are referring to this could be the order id. Still, you will need to take care to pass through the correlation id from commands to events on the command handler side. So, saga id == correlation id == process state id, which is also often used as the primary key for the saga state persistence.

You can also find a good blog post about long-running process implementation using state machines and MassTransit framework in the Chris Patterson's blog post.

Alexey Zimarev
  • 17,944
  • 2
  • 55
  • 83
3

First I should say that I base my answer on my knowledge, experience and my opinion on how should be doing CQRS on PHP

First of all, Process Manager sending a OrderConfirmed event is wrong right? I (as a process manager) cannot send events, only issue commands. Or am I wrong?

Yes. It is wrong. Only Aggregates raise Domain Events and executes Commands. Perhaps Microsoft forgot to show the MarkOrderAsConfirmed command that is sent to the OrderAggregate.

Second, how does the process manager correlate the OrderCreated, SeatsReserved, PaymentReceived events from different aggregates? Is it correlation id that each aggregate honors (and copies over), or is it specific identifiers (e.g. SeatsReserved has an Order Id that refers to the Order Aggregate)?

I use the IDs of the Aggregates that take part in the process. A correlation ID in this case whould map one-to-one with the ID of the OrderAggregate, it has the same life span.

Finally, if it is the case of correlation ids, who creates them? Is it the client who issues a command, like PlaceOrder(order_id, correlation_id)? Is it the Aggregate that accepts the command like PlaceOrder(order_id) and then emits OrderCreated(order_id, corr_id) event? Or, is it the process manager (in some way) that is responsible for this? Alternatively, perhaps correlation ids have nothing to do with this?

In this case I wouldn't use correlation IDs. I use them for debugging purposes only. In my architecture, in order to eliminate code duplication, correlation IDs are generated by the creator of the command (in the Saga) or by the CommandDispatcher (if this is the first command in the process as are all Commands that are not generated by Sagas) and are stored in the generated events as Metadata.

UPDATE: How to use correlation IDs? You select all events from the event store that have that correlation ID and in this way you can "see" the process.

Constantin Galbenu
  • 16,951
  • 3
  • 38
  • 54
  • Thanks. For the last part, would the aggregate need to know about the correlation id? E.g. If `ReservationAggregate` receives a command like `MakeReservation(reservation_id, num_tickets, correlation_id)`, it would need to copy the correlation id to the `ReservationMade(reservation_id, num_tickets, correlation_id)` event? – Andriy Drozdyuk Mar 05 '17 at 08:51
  • 1
    @drozzy In my architecture, I keep corelation ids outside domain layer. They are copied by `CommandDispatcher` from command to events and `Sagas` copy from events to commands. `CommandDispatcher` and `Sagas` are not from domain layer. – Constantin Galbenu Mar 05 '17 at 09:11
  • Only X can emit events and Y cannot - this is very dogmatic. I think people need to build stuff that works, not just blindly follow books without understanding what they are doing. This is an important point - stop doing cargo culting and take use of your own brain to figure out if something can be used or not. – Alexey Zimarev Nov 19 '17 at 10:06
  • @AlexeyZimarev You are entitled to an opinion and I respect that but in DDD there are some good rules to follow . This is one of them. Aggregates are the building blocks that ensures consistency at the lowest level. They are independent. They own their data. They don't need data from the outside in order to make a decision. To disregard that is to not follow DDD which is not bad but it is not DDD. Thanks for your feedback (really!). – Constantin Galbenu Nov 19 '17 at 14:13
  • 1
    Yeah, "this is not DDD" is quite harsh. You can ask Eric Evans about this and he *will not* agree with you, I guarantee. Getting back to business - you messed up domain events and integration events. Event is a universal term. Yes, Sagas do not emit domain events, this would be silly. Unless Saga is an aggregate itself. In any case, *enything* can emit integration events. – Alexey Zimarev Nov 19 '17 at 18:49
  • @AlexeyZimarev You are right about `Aggregates` and `Domain events` so I updated my answer. However, in the context of this question, there are all `Domain events` (`OrderCreated`, `SeatsReserved`, `PaymentReceived`). – Constantin Galbenu Nov 19 '17 at 19:13