Using Domain Events
An issue commonly encountered when working with DDD aggregates, is getting aggregates to interact; with each other, and other services around it.
If you are unfamiliar with the aggregate pattern, Martin Fowler sums it up nicely.
Transactions shouldn't happen between aggregates, they are separate contexts with separate concerns. However this barrier doesn't always make things easy.
We don't want to add a dependency from one aggregate to another.
For example: Let's say when we change a customer’s password, we need to send that customer an email; confirming the change.
What we don't want to do is have our Customer
aggregate take a dependency on some sort of email service. We don't even want the aggregate to depend on some sort of pub/sub library to create and send a message, as that would require some sort of infrastructure dependency. We can however; reverse the dependency, by introducing domain events.
Raising an event
public void ChangePassword(string password)
{
Password = password;
DomainEvents.Raise(new CustomerChangedPassword(this));
}
It's one line of code to raise an event, after the aggregate has done its work. This is where the responsibility ends for the Customer
; and should end. Now anything which cares when a customer password has changed can subscribe to that event and act accordingly.
This pattern allows for loosely coupled objects, which is nice.
Subscribe to the event
Okay, now our Customer
object is shouting whenever there is a password change. Let's send an email to the user; confirming we have changed the password successfully.
We don't want to have to make 'sending an email' a part of the Customer object, this is definitely not its responsibility. Making an intermediary object; something like CustomerPasswordService
or EmailPasswordService
feels messy. We don't need to do this; we have already taken care of our password change, and fired off an event saying that we have changed it. All we need to do now is create a handler
, which subscribes itself to our event.
public class EmailCustomerOnPasswordChangeHandler : IHandle<CustomerChangedPassword>
{
public void Handle(CustomerChangedPassword args)
{
// Send an email to the customer, informing them their password has changed. Put a message on a bus to be handled by another service.
}
}
We can add as many handlers as we want. The DomainEvents
class will find all the Handlers for our CustomerChangedPassword
event; create an instance, and call the Handle method.
Blocking
It's worth noting that your handlers will be called on the same thread as the events were thrown. So these actions will be blocking by default. In a lot of cases (such as the example above) it would be best to put a message on a bus/broker for work to be done elsewhere, or at least make the call asyncronous to negate the blocking.
The implementation
Udi Dahan's static DomainEvents class is what I’ve used here. It has gone through several iterations of criticism and come out a strong implementation.
There are drawbacks to using the static class. Most notably; the handling of our events are called within the same scope as the domain logic. When you raise an event, it's at that point the static class builds/runs all the handlers associated with the event. You might say that this is only a small abstraction on the domain object taking a dependency on a DomainEvents object. If you want to remove this same scope feel from your implementation; Jimmy Bogard talks about a better domain events pattern. In essence, your entities have a collection of IDomainEvent
; which you add events to, then by making use of your ORM (if your using one) hook into SaveChanges and handle all the events in the collection at the time the entity is saved. This way, we have fully encapsulated our domain model, without any reliance on an external service.