I'm trying to implement an Event Store, to be used with Aggregate+Event Sourcing (A+ES), and I've run into I think a gap in understanding.
I'm reading Implementing Domain Driven Design, using both the Domain Event/Event Store and Appendix A: A+ES sections as a template/guide.
In the book, there are two types used, the Event Store, and an IAppendOnlyStore. The examples are in C#.
The book goes on to say
As illustrated in Figure A.14, the class implementing
IEventStoreis a project-specific wrapper around the more generic and reusable
IAppendOnlyStore. While the
IEventStoreimplementation deals with serialization and strong typing, the
IAppendOnlyStoreimplementations provide low-level access to various storage engines.
Implementing Domain Driven Design (Vaughn, p. 563)
IAppendOnlyStore implementations can exist. Some examples the book gives are File-based, MS SQL Server, MySQL, Windows Azure Blob, and MongoDB.
IAppendOnlyStore contains methods that operate on
byte objects. According to the book, the Event Store is responsible for serializing and deserializing a Domain Event into a
byte. The Event Store then passes the serialized
byte to the
IAppendOnlyStore, which then persists the
byte into the persistence store. The
IAppendOnlyStore also has methods for retrieving records from the persistence store. Whether appending or retrieving, a unique identifier is used to indicate which records to retrieve, or which event stream to persist to.
Here's where the gap in my understanding comes in. The example implementation of Relational Persistence in the book uses MySQL. The MySQL
IAppendOnlyStore implementation persists the events in a table, that has columns (Id, Name, Version, Data). Id is a unique, persistence local identity for the record, Name is the Aggregate Identity, Version is the version of the event stream, and Data is a blob, storing the serialized Domain Event. The implementation opens a connection to the database, executes a command, passing in the parameters, and commits the transaction. The
IAppendOnlyStore implementation knows intimate details of the persistence mechanism. For a File-based implementation, it would probably know where the file is; for Azure, it would probably know credentials; for MongoDB it would probably know about the documents required.
The gap is that this seems like a leaky abstraction detail.
It feels like all of those intimate implementation details should be completely encapsulated within the
IAppendOnlyStore implementation. It feels like only the implementation should know that the data is stored in a blob column in MySQL, and would therefore need to be serialized. However, this leaks out of the implementation into the Event Store. The responsibility of serializing and deserializing the Domain Event exists in the Event Store, not the
This feels like the Event Store either knows, or is dictating, that the underlying persistence mechanism will store the Domain Event as a
byte. This feels wrong to me. If we wanted to persist the Domain Event properties as separate columns, in a normalized form in the database, the
IAppendOnlyStore implementation should support that. It already supports different types of connections; it knows whether it's executing a raw query or a stored procedure; whether one table is updated or multiple; so it should have the freedom to interact with the underlying mechanism in whatever way is necessary. This, to me, implies that it is actually the responsibility of the
IAppendOnlyStore to serialize the Domain Event, not the Event Store, because the
IAppendOnlyStore is the only component that knows, or should know, what the underlying persistence mechanism requires, in terms of format, datatype, and data shape. In this case, that the Domain Event is serialized as a
byte, and stored in a single blob column, in a single table. I'll call this Assumption One.
With Assumption One in mind, it is also implied that deserialization is the responsibility of the
IAppendOnlyStore, as it is the opposite pairing of serialization. If the
IAppendOnlyStore is responsible for serializing an in-memory Domain Event instance, into a
byte for the underlying persistence mechanism, it seems reasonable that it would also have the responsibility of deserializing a
byte from the underlying persistence mechanism, into an in-memory Domain Event instance. I'll call this Assumption Two.
With Assumption Two in mind, all transformation from in-memory object, to persisted object, and back, is the responsibility of the
IAppendOnlyStore supports retrieving all, or some, events associated with a given Aggregate Identity. In the example in the book, the Event Store translates an
IEnumerable<DataWithVersion> from the
IAppendOnlyStore, into an
EventStream containing a list of the deserialized events. With Assumption One and Two in mind, this would not be an operation on the Event Store, as that responsibility would lie with the
So from one single feeling, that the
IAppendOnlyStore should have exclusive knowledge of how a Domain Event is persisted, nearly all responsibility has been removed from the Event Store. It doesn't deal with serialization; deserialization; enforcing strong typing; retrieving events, or appending them; translating from persistence object to in-memory object, or vice-versa. At this level, it's delegating all of those concerns to the
IAppendOnlyStore. All that's left is concurrency protection, but even that seems more like a concern for the storage mechanism, than the Event Store.
Did I miss something? At this point, the only reason I can think of that would justify the existence of the Event Store, is to provide a non-storage/non-persistence/non-lowest-level oriented interface that indicates that an event is stored somewhere, somehow. It feels like its only purpose is to delegate calls to whatever instance of
IAppendOnlyStore it's been told to use, to relieve the client of knowing that it is interacting directly with the persistence mechanism.
Is there some other reason to keep these responsibilities in the Event Store that I missed from my reading of the book? Should the Domain Events really be serialized in the Event Store, as opposed to the persistence mechanism? Is it preferable for the Event Store to guarantee or enforce that the underlying mechanism persists events as serialized binary blobs?