In our application we raise domain events when something changes in the domain model. Some of the tasks that are performed by the event handlers must be done within the same transaction that was used when the event is raised, other tasks must be performed outside of this transaction.
For example,
When an Orderline is added to an Order entity, the OrderLineAdded domain event is raised, one domain event changes the state of the domain model (so must be performed in the same transaction), then when the transaction is completed the UI must be updated.
How would you approach this problem?
Option 1 seems confusing, since the events names must somehow convey they are in or out of a transaction, but with option 2 handlers of the domain event must always assume that they are called synchronously from within a transaction.
Maybe there is a better approach?
Handling the domain events is an application concern. The domain model layer should only focus on the domain logic—things that a domain expert would understand, not application infrastructure like handlers and side-effect persistence actions using repositories.
In domain-driven design, domain events are described as something that happens in the domain and is important to domain experts. Such events typically occur regardless of whether or to what extent the domain is implemented in a software system. They are also independent of technologies.
I've had a similar problem. Domain model was publishing events (using the technique Udi Dahan describes here). Then I realized that my UI-related handlers are invoked even if something goes wrong and transaction is rolled back later.
To fix this I introduced another role to the system, another kind of event handler. I has ITransactionalEventHadneler
and INonTransactionalEventHandler
. The former were invoked synchonously immediately in DomainEvents.Publish()
method. The latter were queued to be invoked as soon as transaction gets committed (using System.Transactions hooks). The solution worked fine and was quite readable and maintainable.
I think both approach could be good, just stick to the same approach in every part of your code:
I personally like option 2 more, because it keeps the domain code cleaner and by using asynchronous communication the core and other modules will be decoupled, therefore problems in the external modules will not hinder the workings of the core. On the other hand, there may be circumstances where option 1 is more advantageous.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With