Handling Events on the Read Side
I said that our Read Model Projections are special kinds of Event Handlers that are registered to the Bus. What makes them unique? Their role is to simply update the Read Model persistence mechanism. This persistence mechanism is optimized for Reads. It might be a denormalized SQL Server table, Redis, OLAP Cube, Neo4J, etc.
While this example is simple, being that Nick just needs to know what Roast Schedules he has created, you can probably imagine the unique requirements of First Pop Coffee’s User Interface. Nick needs to be able to make the right decisions about the management of his coffee roasting business. This is the deciding factor in how the Team will shape and decide what type of persistence infrastructure is best for each Read Model - what does Nick need to know to make a decision?
For illustration purposes, we’re going to build a quick in-memory implementation of the Roast Planning Read Model. In future posts, we’ll come back and look into details around production-level solutions.
For the Read Model’s persistence store, we’ll start with a vanilla Entity Framework DbContext. Another, more performant, solution would be Dapper by Stack Exchange but for now we’re going to use Entity Framework with fake, in-memory DbSet
instances. The implementation of FakeDbSet
I’m using comes from the article Entity Framework Testing with your Own Test Doubles (EF6 onwards).
The beauty of building this application with CQRS in mind from the beginning is that we can forget about the excessive DTO mapping that we’ve been used to when our Write Model is our Read Model as well. In the traditional CRUD architecture we know so well, DbContext
is queried from and the resulting Entities are mapped to View Model DTOs for the Presentation Layer. There’s nothing wrong with this architecture, however through CQRS we’ve enabled ourselves to reduce the impedence mismatch between our DTOs and our Domain Model persistence store. Our database can now store flat, denormalized-for-fast-reads objects.
We can simply store RoastScheduleViewModel
in our DbContext
like so:
public interface IRoastPlanningContext
{
DbSet<RoastScheduleViewModel> RoastSchedules { get; }
int SaveChanges();
}
public class RoastPlanningContext : IRoastPlanningContext
{
public DbSet<RoastScheduleViewModel> RoastSchedules { get; set; }
public RoastPlanningContext(FakeDbSet<RoastScheduleViewModel> roastSchedules)
{
this.RoastSchedules = roastSchedules;
}
public int SaveChangesCount { get; private set; }
public int SaveChanges()
{
this.SaveChangesCount++;
return 1;
}
}
This DbContext is only going to be used by our Read Model Projections to insert data into our Read Model SQL Database upon handling new Events emitted by the Event Store in our Write Model.
public class RoastScheduleView : IEventHandler
{
private readonly IRoastPlanningContext _roastPlanningContext;
public RoastScheduleView(IRoastPlanningContext roastPlanningContext)
{
_roastPlanningContext = roastPlanningContext;
}
public void Handle(RoastScheduleCreatedEvent message)
{
var newRoastSchedule = new RoastScheduleViewModel(message.RoastScheduleId, message.RoastWeekStartsOn);
_roastPlanningContext.RoastSchedules.Add(newRoastSchedule);
_roastPlanningContext.SaveChanges();
}
}
Although this is how we’re making our Read Model consistent with our Write Model, we’re not going to use IRoastPlanningContext
for our Presentation Layer. We’re going to use what’s called a Read Model Facade. We’ll cover that in the next post. Stay tuned!
I will be releasing most of this content only to subscribers. Make sure you sign up by clicking the big red button!
Related Posts:
- Projection
- Publishing Events to the Bus
- Testing the Bus
- Sending Commands to the Bus
- The Bus
- The Read Model
- An Executable Specification
- Testing an Event Sourced Aggregate Root
- Aggregate Event Persistence
- Event Store
- 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