I\'ve been studying on domain driven design in conjunction with domain events. I really like the separations of concerns those events provide. I ran into an issue with the order
Either your (transactional) event handlers enlist in the (potentially distributed) transaction, or you publish/handle the events after the transaction committed. Your "QueueEvents" solution gets the basic idea right, but there are more elegant solutions, like publishing via the repository or the event store. For an example have a look at SimpleCQRS
You might also find these questions and answers useful:
CQRS: Storing events and publishing them - how do I do this in a safe way?
Event Aggregator Error Handling With Rollback
Update on point 3:
... however I read somewhere that domain events should not rely on a specific order of execution.
Regardless of your way of persisting, the order of events absolutely matters (within an aggregate).
Now NotifyCustomer behaviour depends on the order being saved in the database, so the PersistOrder event handler should execute first. Is it acceptable that these handlers introduce a property for example that indicates the order of their execution?
Persisting and handling events are separate concerns - don't persist using an event handler. First persist, then handle.
Disclaimer: I don't know what I am talking about. ⚠️
As an alternative to raising events in the ShoppingCartService
you could raise events in the OrderRepository
.
As an alternative to queuing domain events in the ShoppingCartService
you could queue them in the Order
aggregate by inheriting from a base class that provides a AddDomainEvent
method.
public class Order : Aggregate
{
public Order(IEnumerable<ShoppingCartItem> cart, Customer customer)
{
AddDomainEvent(new OrderCreated(cart, customer))
}
}
public abstract class Aggregate
{
private List<IDomainEvent> _domainEvents = new List<IDomainEvent>();
public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
public void AddDomainEvent(IDomainEvent domainEvent)
{
_domainEvents.Add(domainEvent);
}
}