I've been using the Domain Events pattern for some time - it enables us to encapsulate as much behaviour in our domain layer as possible and provides a nice way for other parts of our application to subscribe to domain events.
Currently we're using a static class that our domain objects can call to raise events:
static class DomainEvents
{
public static IEventDispatcher Dispatcher { get; set; }
public static void Raise<TEvent>(TEvent e)
{
if (e != null)
{
Dispatcher.Dispatch(e);
}
}
}
As you can see, this is little more than a shim to an IEventDispatcher
that actually does the work of dispatching or publishing the events.
Our dispatcher implementation simply uses our IoC container (StructureMap) to locate event handlers for the specified type of event.
public void Dispatch<TEvent>(TEvent e)
{
foreach (var handler in container.GetAllInstances<IHandler<TEvent>>())
{
handler.Handle(e);
}
}
This works okay in most cases. However, there are a few problems with this approach:
Events should only be dispatched if the entity is successfully persisted
Take the following class:
public class Order
{
public string Id { get; private set; }
public decimal Amount { get; private set; }
public Order(decimal amount)
{
Amount = amount;
DomainEvents.Raise(new OrderRaisedEvent { OrderId = Id });
}
}
In the Order
constructor we raise an OrderRaisedEvent
. In our application layer we'd likely create the order instance, add it to our database "session" and then commit/save the changes:
var order = new Order(amount: 10);
session.Store(order);
session.SaveChanges();
The problem here is that the domain event is raised before we have successfully saved our Order entity (committing the transaction). If the save failed we would have still dispatched the events.
A better approach would be to queue the events until the entity is persisted. However, I'm unsure how best to implement this whilst maintaining strongly typed event handlers.
Events should not be created until the entity is persisted
Another issue I'm facing is that our entity identifiers are not set/assigned until store the entity (RavenDB - session.Store
). This means that in the above example, the order identifier passed to the event is actually null
.
Since I'm not sure how can actually generate RavenDB identifiers upfront, one solution could be to delay the creation of the events until the entity is actually saved but again I'm not how best to implement this - perhaps queuing a collection of Func<TEntity, TEvent>
?
One solution (as suggested by @synhershko) is to move the dispatching of the domain events outside of the domain. This way we can ensure our entity is persisted before we raise any events.
However, we're now moving behaviour out of the domain (where it belongs) into our application just to work around our persistence technology - which I'm not that happy about.
My solution to Events should only be dispatched if the entity is successfully persisted was to create a deferred event dispatcher that queues the events. We then inject the dispatcher into our unit of work ensuring that we first persist/save our entity and then emit the domain events:
Essentially this achieves the same thing as suggested by @synhershko but keeps the "raising" of events within my domain.
As for Events should not be created until the entity is persisted the main issue was that entity identifiers were being set externally by RavenDB. A solution that keeps my domain persistent ignorant and easy to test is to simply pass the id as a constructor parameter. This is what I would have done if using a SQL database (usually passing a Guid).
Fortunately RavenDB does provide a way for you to generate identifiers using the hilo strategy (so we can keep RESTful identifiers). This is from the RavenDB Contrib project:
I can then use this to generate an ID and pass it in my entity constructors: