Event Store
We know that the creation of a RoastSchedule
Aggregate means persisting it. This is where the Repository pattern comes into play. Since we are going to use Event Sourcing, our concrete RoastSchedule
Repository will look slightly different from the traditional Collection-Oriented and Persistence-Oriented Repositories discussed by Vaughn Vernon in Implementing Domain-Driven Design. Our generic Repository Interface will, however, look very similar to a Persistence-Oriented Repository in that it requires a call to Save()
to actually push objects down to storage.
public interface IRepository<T> where T : AggregateRoot, new() {
void Save(AggregateRoot aggregate, int exptectedVersion);
T GetById(Guid id);
}
We won’t worry about the generic type constraint new()
(requiring that the concrete RoastSchedule
have a public constructor public RoastSchedule()
) for right now, as it has to do with the way that we re-constitute Aggregates using our Event Sourced Aggregate Root from our Repository.
Now that we have our generic Repository, we are going to design a concrete Repository for an Event Sourced Aggregate. This is where we introduce the idea of an Event Store. Our Event Store is going to be the book of truth for our Event Sourced Aggregates, like RoastSchedule
. This will be pretty much a copy/paste from Greg Young’s SimpleCQRS’s in-memory Event Store implementation found here: https://github.com/gregoryyoung/m-r/blob/master/SimpleCQRS/EventStore.cs. It’ll only be temporary until we start looking at real Event Storage tools out there.
Notice that IEventStore
has two methods: SaveEvents
and GetEventsForAggregate
:
public interface IEventStore {
void SaveEvents(Guid aggregateId, IEnumerable<Event> events, int expectedVersion);
List<Event> GetEventsForAggregate(Guid aggregateId);
}
We want to have the RoastScheduleCreated
Domain Event that is emitted from our RoastSchedule
Aggregate as a result of the CreateRoastSchedule
Command persist to our in-memory Event Store. We also want to have the ability to replay all previous Domain Events emitted by the RoastSchedule
Aggregate to build the state of the RoastSchedule
Aggregate.
Notice the expectedVersion
parameter to Save
in our IRepository<T>
and SaveEvents
in our IEventStore
interfaces. This is one of the bigger differences between this and a more traditional Repository; this idea of version
. When we’re storing Domain Events with the intention to be able to use them to figure out the state of an Aggregate, sequence becomes important. You only have to imagine debits and credits to a bank account being replayed in a different order to understand the issue.
Further, we have named the parameter expectedVersion
. When we try to save an Event to the Event Store, we can ask the Event Store what version the Aggregate should be at this moment. This is a check against the Event Store to make sure that the Event we are saving to it is truly the next one to be applied to the Aggregate; hence expectedVersion
.
I’ll show you how we use the Event Store and Repository in accordance with our Aggregate Root over the next couple posts.
I will be releasing most of this content only to subscribers. Make sure you sign up by clicking the big red button!
Related Posts:
- Command Handlers
- Implementing an Event Sourced Aggregate
- Design Level Continued
- Complexity and Cost
- Design Level EventStorming
- People and Commands
- Hotspots
- Domain Discoveries
- Big Picture EventStorming
- The Domain - First Pop Coffee Company
- More Efficient Domain Modeling with EventStorming
- Where do you find resources for learning DDD, CQRS, Event Sourcing, and EventStorming?
- Has the code devolved into a big ball of mud?… What can you do about it?
- A Better Way to Project Domain Entities into DTOs
- Exposing IQueryable in a CQRS Query Stack
- A Pattern to Decouple your Aggregates from their Clients
- Erlang-style Supervisors in C# with Akka.NET and the Actor Model