.NET Backend Entity Framework SQL

CQRS/ES #6 Read database and Event Handlers

As I announced in the last part, our CQRS/ES journey is almost finished! But before it happens we need to take care of read side of our application. However, before we move forward to the implementation, it’s worthwhile to explain why do we need a read side? After all, we have an excellent data source called Event Store which allows us to reconstruct every domain object in our system. What’s, even more awesome is the fact that we can „time travel” in our domain by not applying all events in our domain objects. It sounds like a perfect solution for every scenario that may happen. Well not exactly. Imagine that your task is to create a dashboard with some statistics. In most cases, that would require getting a lot of specific data from the database. Now, someone would say that’s not a problem since we can add some „WHERE, GROUP BY, HAVING” etc. but here is the thing. Our Event Store does not store the current state of domain. It keeps only events which first need to be applied inside domain objects. That means that we would need to make a projection of each domain object and then filter them in memory. Even with snapshots, that sounds ridiculous. The conclusion is simple. Event sourcing is not a right approach when we need to get a lot of domain objects at the same time. It’s just not efficient. So, how can we deal with such a problem? We need a second database optimized for reading!

 

Creating a read database

Knowing the problem, we can start thing about the structure of read database. We already know that anything similar to Event Store just won’t work. But let me ask you a question. How would you design a common database for our calendar? All we have is a just an event with some cycles. That sounds like a „One to Many” relation, right? So, why don’t we use a typical „Current state” database for reading? The images below show the structure:

 

model

 

Here’s the implementation of that tables and database context using Entity Framework:

 

public class CalendarItemEntity : InternalEntity
{
    public CalendarItemEntity()
    {
        Cycles = new HashSet<CalendarItemCycleEntity>();
    }

    public CalendarItemEntity(Guid id) :this()
    {
        Id = id;
    }

    public string UserId { get; set; }

    public string Name { get; set; }

    public string Description { get; set; }

    public DateTime StartDate { get; set; }

    public DateTime EndDate { get; set; }

    public ICollection<CalendarItemCycleEntity> Cycles { get; set; }
}
public class CalendarItemCycleEntity : InternalEntity
{
    public CalendarItemCycleEntity()
    {
        Id = Guid.NewGuid();
    }
    public Guid CalendarItemId { get; set; }

    [ForeignKey("CalendarItemId")]
    public CalendarItemEntity CalendarItem { get; set; }

    public DateTime StartDate { get; set; }

    public DateTime? EndDate { get; set; }

    public CalendarItemCycleType Type { get; set; }

    public int Interval { get; set; }
}
public class ReadSideContext : DbContext
{
    static ReadSideContext()
    {
        Database.SetInitializer(new DropCreateDatabaseIfModelChanges<ReadSideContext>());
    }

    public ReadSideContext() :base(nameof(ReadSideContext))
    {
        
    }

    public DbSet<CalendarItemEntity> CalendarItems { get; set; }

    public DbSet<CalendarItemCycleEntity> CalendarItemCycles { get; set; }
}

 

If you read the second part of this series, you probably notice that we used that structure to model our domain objects. Why didn’t we create an Event Store first? Because it’s not natural for human reading. It comfortable structure for having all events in one table, but it’s pretty hard to understand the relations inside our domain. The structure above does that really well. Okay, now we are having a two databases. One optimized for storing events and second optimized for reading with clear relations inside. The question is, how do we synchronize them? And that’s where events and event handlers come to play.

 

Event Handlers

Our domain objects produce the series of events which then we save into Event Store. So, why don’t we use those events to inform our read side that some data changed and we should modify it? That seems quite easy since we already have an event bus, but we need one more thing. We need a class which receives proper event and then modifies read database based on event’s type. That is Event Handler’s responsibility. The implementation for sample handler is given below:

 

public interface IEventHandler<in TEvent> where TEvent : class, IEvent
{
    Task HandleAsync(TEvent @event);
}
public class CalendarItemCreatedEventHandler : IEventHandler<CalendarItemCreatedEvent>
{
    ICalendarItemRepository CalendarItemRepository { get; }

    public CalendarItemCreatedEventHandler(ICalendarItemRepository calendarItemRepository)
    {
        CalendarItemRepository = calendarItemRepository;
    }

    public async Task HandleAsync(CalendarItemCreatedEvent @event) =>
        await CalendarItemRepository.AddAsync(new CalendarItemEntity
        {
            Id = @event.AggregateId,
            UserId = @event.UserId,
            Name = @event.Name,
            Description = @event.Description,
            StartDate = @event.StartDate,
            EndDate = @event.EndDate
        });
    
}

 
Let me now explain what happened here. CalendarItemCreatedEventHandler implements a generic interface which parameter of IEvent type. This interface delivers only one asynchronous method called HandleAsync which receives an event and process that. As you probably noticed for inserting a new row into the database, I used a repository pattern which is not mandatory. You can inject EF context directly if it suits you more. Overall event handler looks quite similar to command handler, but there’s a little difference. The event handler doesn’t validate an event before processing it. Why? Because event represents something that already happened in our system. We can’t change that, so we don’t have to validate that. Okay, so I guess that that’s it. That was the last piece in our journey!

 

Why CQRS + ES is great combo?

Before we end this series it worthwhile to explain why CQRS and Event Sourcing are the great complement of each other. The power that stands behind ES is the fact that we track all data that have ever been produced inside our system. We know everything, and we can time travel to the past to know how our domain looked e.g. a few years ago. That’s insane! Moreover collecting this whole data has one more advantage. You’ll never be surprised by a business. Sometimes it happens to me that after the implementation of new functionality my PM come and ask me, „How would it affect our earnings if we decided on this two years ago?” If you keep the only current state of your domain, you can’t create any simulation. With ES it’s super easy and believe me that your PM will love you for that 😉 But ES also has a drawback which is poorly dealing with a lot of data. That’s where CQRS comes to play, right? It’s a pattern which separates read side of application from the write side. We can scale them independently but what’s even more important; we can optimize each side (using technology and data structures) for these two operations. That’s why I would recommend using ES and CQRS together.

 

Summary

Well, I guess that we’re done! We got acquired with the concept of CQRS and ES, and we implemented every piece inside our awesome calendar. Remember that whole project was just an example, one way of dealing with CQRS and ES. There’re a lot of libraries that may help you to implement that way faster than I did (for instance NEventStore). I hope that you enjoyed that series and that it helped you somehow. Like always I encourage you to follow me on a Twitter and Facebook just to be up t date with new, upcoming posts on this blog.