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:
public class DeferredEventDispatcher : IEventDispatcher
{
private readonly IEventDispatcher inner;
private readonly ConcurrentQueue<Action> events = new ConcurrentQueue<Action>();
public DeferredEventDispatcher(IEventDispatcher inner)
{
this.inner = inner;
}
public void Dispatch<TEvent>(TEvent e)
{
events.Enqueue(() => inner.Dispatch(e));
}
public void Resolve()
{
Action dispatch;
while (events.TryDequeue(out dispatch))
{
dispatch();
}
}
}
public class UnitOfWork
{
public void Commit()
{
session.SaveChanges();
dispatcher.Resolve(); // raise 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:
public static string GenerateIdFor<T>(this IAdvancedDocumentSessionOperations session)
{
// An entity instance is required to generate a key, but we only have a type.
// We might not have a public constructor, so we must use reflection.
var entity = Activator.CreateInstance(typeof(T), true);
// Generate an ID using the commands and conventions from the current session
var conventions = session.DocumentStore.Conventions;
var databaseName = session.GetDatabaseName();
var databaseCommands = session.GetDatabaseCommands();
return conventions.GenerateDocumentKey(databaseName, databaseCommands, entity);
}
I can then use this to generate an ID and pass it in my entity constructors:
var orderId = session.GenerateIdFor<Order>();
var order = new Order(orderId, 1.99M);