In many different projects I have seen 2 different approaches of raising Domain Events.
Raise Domain Event directly from aggregate. For example imagine you have Customer aggregate and here is a method inside it:
public virtual void ChangeEmail(string email) { if(this.Email != email) { this.Email = email; DomainEvents.Raise<CustomerChangedEmail>(new CustomerChangedEmail(email)); } }
I can see 2 problems with this approach. The first one is that the event is raised regardless of whether the aggregate is persisted or not. Imagine if you want to send an email to a customer after successful registration. An event "CustomerChangedEmail" will be raised and some IEmailSender will send the email even if the aggregate wasn't saved. The second problem with the current implementation is that every event should be immutable. So the question is how can I initialize its "OccuredOn" property? Only inside aggregate! Its logical, right! It forces me to pass ISystemClock (system time abstraction) to each and every method on aggregate! Whaaat??? Don't you find this design brittle and cumbersome? Here is what we'll come up with:
public virtual void ChangeEmail(string email, ISystemClock systemClock) { if(this.Email != email) { this.Email = email; DomainEvents.Raise<CustomerChangedEmail>(new CustomerChangedEmail(email, systemClock.DateTimeNow)); } }
The second approach is to go what Event Sourcing pattern recommends to do. On each and every aggregate, we define a (List) list of uncommited events. Please payAttention that UncommitedEvent is not a domain Event! It doesn't even has OccuredOn property. Now, when ChangeEmail method is called on Customer Aggregate, we don't raise anything. We just save the event to uncommitedEvents collection which exists on our aggregate. Like this:
public virtual void ChangeEmail(string email) { if(this.Email != email) { this.Email = email; UncommitedEvents.Add(new CustomerChangedEmail(email)); } }
So, when does the actual domain event is raised??? This responsibility is delegated to persistence layer. In ICustomerRepository we have access to ISystemClock, because we can easily inject it inside repository. Inside Save() method of ICustomerRepository we should extract all uncommitedEvents from Aggregate and for each of them create a DomainEvent. Then we set up OccuredOn property on newly created Domain Event. Then, IN ONE TRANSACTION we save the aggregate and publish ALL domain events. This way we'll be sure that all events will will raised in transnational boundary with aggregate persistence.
What I don't like about this approach? I don't want to create 2 different types for the same event, i.e for CustomerChangedEmail behavior I should have CustomerChangedEmailUncommited type and CustomerChangedEmailDomainEvent. It would be nice to have just one type. Please share your experience regarding to this topic!
OccuredOn
property - this can be set by some kind of centralized event factory (or even directly by domain publisher), so you don't have to duplicate your code in every aggregate. – Croce@TransactionalEventListener
. I'm sure it's very similar in C# world. You can search for Jimmy Bogard's articles - he's the guy who writes a lot about DDD in C#/.net context. Oh, and regarding factory - you can use factory inside your domain entities (factory can be a domain concept). – CroceISystemClock
explicitly, which is clearly not part of your domain. Therefore, you can create a factory which adheres to your domain language. This way, your event factory is explicitly part of your domain and hides all the infrastructure details (like system clock) from your domain entities. You inject your domain factory when creating your domain entities. BTW, Vaughn Vernon included a chapter about factory methods in his book - goo.gl/u9cwdO. – Croce@Configurable
annotation and AspectJ in Spring to inject your factory into aggregate entities. I'm sure there's a similar solution for C#. – Croce