The event storage is basically where you persist all the events your domain is publishing. The only thing that makes it a little bit tricky to implement is the fact that you need to worry about concurrency. Concurrency is tricky, but not that tricky.
Event storages can be created using various technologies. According to Greg the best way is to write the events onto the disc, circumventing the file system. Of course then you’ll need to write your own indexing as well. That’s just crazy stuff and I’m not digging into those APIs any time soon :p In this example I’ll be showing an event storage for a sql database. We need to be able to store our serialized events and keep track of what aggregate root published the event. Also we need to maintain the concurrency version of the aggregate root.
Database
First let’s look at the database schema
EventProviders
EventProviderId (uniqueidentifier)
Type (nvarchar)
VersionNumber (int)
Events
FkEventProviderId (uniqueidentifier)
DateTime (datetime)
Data (varbinary)
The name “event provider” refers in this case to the aggregate root. Each event provider has an id, a type name and a version number. The version number is used to do optimistic locking, making sure we don’t update an aggregate root that has been changed in the meantime. The version number also serves another task; the version number is always equal to the number of events published by the aggregate root.
Now let’s look at the events table. Each event has a reference to the event provider that published the event. We also store a serialized version of the event and the time it was persisted. Notice we have no “eventId”. We don’t need one.
Event storage
We will access the event storage using the IEventStorage interface.
public interface IEventStorage
{
IEnumerable GetAllEventsForEventProvider(Guid id);
void Save(IEventProvider provider);
}
To save an aggregate root we pass the a reference to the IEventProvider interface (implemented by the aggregate root) to the event storage.
public interface IEventProvider
{
IEnumerable GetChanges();
void ClearChanges();
Guid Id { get; set; }
int Version { get; set; }
}
The Save method
To save the IEventProvider (i.e. Aggregate Root) we need to do the following
- Start transaction
- Get the event provider from the database
- If the event provider does not exist in the database, create it
- If the event provider exists, check that the database version matches the current version of the aggregate root. If not throw a concurrency exception.
- Save all events the event provider has published after previous version
- Update the version number in the event provider. The version number is now the previous version number plus the number of events published since previous version.
- End transaction
The implementation is pretty straight forward. As you see all the complexity in this operation comes from the fact that we need to check for concurrency violations.
Included project
I am including a project with an implementation of the event store and some other things like the repository and the aggregate root base class. This was written quickly at the DDD Course by me and some other peeps, and is far from production ready and it has no UI. However I think it’s better to post this (crappy) code than to post nothing at all. The idea is that you can have some code to look at. There are some integration tests you can run if you set up the database. If you want to use an implementation like this, you can get some speed optimizations by using stored procedures. It’s evil, but still.
Some things to keep in mind
I have not talked about implementing snapshots to improve performance but it is really not that hard. I would use the Memento Pattern and not store the actual aggregate root. From there on it is just plankekjøring ;) Mark Nijhof is publishing an extensive example at some point in the near or far future.