Reputation:
As far as my understanding goes there are two types of events in DDD. Ran under same process and across multiple process.
How should the event subscription and dispatching be handled ?
I'll provide an example for an order being placed. When you place an order (1st bounded context) you have to update a stock (2nd BC).
So in the OrderAggregate you would have some method that creates an order and in the end it will add an event, OrderPlacedEvent for example, to a list of Domain.Events.
Now Assuming that in the 1st bounded context you will send a confirmation email. Who should notify the SendEmailEventHandler to actually send the email ?
So who is responsible for DISPATCHING the event to all interested parties, in this case SendEmailEventHandler ?
Moreover since the transaction should be extended to 2nd bounded context, who should dispetch the event ? A message bus I know, but if you don't have one ?
The way I've design right now is that in my Domain.Aggregate I have an AddEvent(IDomainEvent event) method which is made available by the AggregateRoot class (base class for a domain aggregates).
When an aggregate does something it will add to that list the event from a specific action.
StartOrder() => OrderAggregate Method
OrderStartedEvent() => DomainEvent
In the Application.Layer I have a UseCaseHandler (the play-ground) where I invoke aggregate methods and repositories (that is all).
In Infrastructure I have a Repository which abstracts the ORM and provided only methods for Add, Update and Delete + a method that will get the aggregate by it's id.
From my current design the repository is the one that dispatches the events before committing.
So this is how the repo looks like
protected override async Task AddAsync(TAggregate aggregate, CancellationToken token = default(CancellationToken))
{
using (ITransaction transaction = Session.BeginTransaction())
{
await Session.SaveAsync(aggregate, aggregate.Id, token);
/// => dispatch domain.Event to all interested parties
try
{
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
1st Doubt : Now this part represents the dispatching, of which I'm not fully sure. Should the Repository be in charge of dispatching, from my perspective it should but still I'm not sure ?
And this is how the Application UseCaseHandler looks
private IOrderRepository _orderRepository;
public async Task HandleAsync(CreateOrderRequest request, CancellationToken? cancellationToken = null)
{
OrderAggregate aggregate = await _orderRepository.GetByIdAsync(request.Id);
aggregate.CreateDraftOrder(); /// => adds the OrderPlacedEvent()
await _orderRepository.CreateOrderAsync(aggregate);
}
The 2nd Unknown: Where and who should handle the subscription more importantly ?
Upvotes: 1
Views: 471
Reputation: 57194
You want to review Reliable Messaging with Distributed Transactions, by Udi Dahan.
Summary: if you need reliable delivery of messages across boundaries, then you need to design your system such that outbound messages are stored durably with the changes to the domain model, you need retry plumbing that will send all messages that haven't been acknowledged, you need clear identity semantics on your messages so that consumers can recognize duplicates, and you need consumers that do sensible things when receiving a second copy of a message.
See also: Life Beyond Distributed Transactions, by Pat Helland.
Upvotes: 1