5

In the DDD litterature, the returning domain event pattern is described as a way to manage domain events. Conceptually, the aggregate root keeps a list of domain events, populated when you do some operations on it.

When the operation on the aggregate root is done, the DB transaction is completed, at the application service layer, and then, the application service iterates on the domain events, calling an Event Dispatcher to handle those messages.

My question is concerning the way we should handle transaction at this moment. Should the Event Dispatcher be responsible of managing a new transaction for each event it process? Or should the application service manages the transaction inside the domain event iteration where it calls the domain Event Dispatcher? When the dispatcher uses infrastructure mecanism like RabbitMQ, the question is irrelevent, but when the domain events are handled in-process, it is.

Sub-question related to my question. What is your opinion about using ORM hooks (i.e.: IPostInsertEventListener, IPostDeleteEventListener, IPostUpdateEventListener of NHibernate) to kick in the Domain Events iteration on the aggregate root instead of manually doing it in the application service? Does it add too much coupling? Is it better because it does not require the same code being written at each use case (the domain event looping on the aggregate and potentially the new transaction creation if it is not inside the dispatcher)?

Normand Bedard
  • 2,375
  • 2
  • 15
  • 18

2 Answers2

3

My question is concerning the way we should handle transaction at this moment. Should the Event Dispatcher be responsible of managing a new transaction for each event it process? Or should the application service manages the transaction inside the domain event iteration where it calls the domain Event Dispatcher?

What you are asking here is really a specialized version of this question: should we ever update more than one aggregate in a single transaction?

You can find a lot of assertions that the answer is "no". For instance, Vaughn Vernon (2014)

A properly designed aggregate is one that can be modified in any way required by the business with its invariants completely consistent within a single transaction. And a properly designed bounded context modifies only one aggregate instance per transaction in all cases.

Greg Young tends to go further, pointing out that adhering to this rule allows you to partition your data by aggregate id. In other words, the aggregate boundaries are an explicit expression of how your data can be organized.

So your best bet is to try to arrange your more complicated orchestrations such that each aggregate is updated in its own transaction.

My question is related to the way we handle the transaction of the event sent after the initial aggregate is altered after the initial transaction is completed. The domain event must be handled, and its process could need to alter another aggregate.

Right, so if we're going to alter another aggregate, then there should (per the advice above) be a new transaction for the change to the aggregate. In other words, it's not the routing of the domain event that determines if we need another transaction -- the choice of event handler determines whether or not we need another transaction.

VoiceOfUnreason
  • 40,245
  • 4
  • 34
  • 73
  • 1
    Not sure why we are talking about modyfing more than one aggregate per transaction here. My question is related to the way we handle the transaction of the event sent after the initial aggregate is altered after the initial transaction is completed. The domain event must be handled, and its process could need to alter another aggregate. When sending domain event that is handled in another bounded context, using messaging system forces a new transaction in another process, but when the domain event is handled inside the same bounded context, without messaging system, it does not. – Normand Bedard Nov 13 '17 at 12:15
2

Just because event handling happens in-process doesn't mean the originating application service has to orchestrate all transactions happening as a consequence of the events.

If we take in-process event handling via the Observable pattern for instance, each Observer will be responsible for creating its own transaction if it needs one.

What is your opinion about using ORM hooks (i.e.: IPostInsertEventListener, IPostDeleteEventListener, IPostUpdateEventListener of NHibernate) to kick in the Domain Events iteration on the aggregate root instead of manually doing it in the application service?

Wouldn't this have to happen during the original DB transaction, effectively turning everything into immediate consistency (if events are handled in process)?

guillaume31
  • 12,725
  • 28
  • 43
  • Not sure to understand your question. With NHibernate, you can be "notified" when the transaction completes, implementing the IPostInsertEventListener, IPostDeleteEventListener, IPostUpdateEventListener interfaces. This is what I called hooks. One option is to use those hooks to kick-in the event dispatching. The other option is to not rely on the ORM hooks, and to kick-in the event directly in the service, sequentially after you close/dispose the transaction. – Normand Bedard Nov 14 '17 at 12:38
  • Also, I agree with hangling the new transation inside the handlers. This way, if one day a full messaging system (out of process) is used for the domain events, handlers will be already ready to manage the processing of the event correctly. – Normand Bedard Nov 14 '17 at 12:40
  • Oh, I thought Post... hooks were triggered before the transaction completed, allowing you to sneak in stuff between the SQL command and the final commit. My bad. – guillaume31 Nov 14 '17 at 14:03