“Event Sourcing, CQRS? Sounds familiar, but what is it again?” If you are in the same state of mind and the above sounds just slightly familiar or not at all, this is the right post for you. Event Sourcing and CQRS are two patterns on its own that apparently play very nicely together. Let’s look at each individually and then it will become easy to see how they complement each other.
Event Sourcing is a pattern when everything that happens in a system is an event. All events are stored in the sequence they were created. For example, Order Created, Payment Completed, Order Shipped and Order Delivered. Event carries information about state transition of an entity, e.g. Order. To reconstruct latest state of an entity we need to re-play all events strictly in the order they were created. Events are immutable, after event is emitted it is stored and never changes. (If you are wondering what is an entity, it is a Domain Driven Design term and you can read more about it here.)
For example, on a diagram below we can see three Orders and events associated with each Order. The Orders are in different state. Notice how events carry data and how we can use them to reconstruct each Order’s status.
To implement Event Sourcing pattern, we need to be able to store, send and receive events. For storing it is best if you can use a dedicated event store database. Second and still good option is relational database, e.g.PostgreSQL, MySQL. For sending and receiving events we need persistent messaging solution that guarantees at least one delivery, for example RabbitMQ or Kafka.
Below is an example of how Order tables can be organized to store Order events. Events are always inserted, even to cancel an order we do not delete order record, but rather create ORDER CANCELLED event. To reconstruct Order 777 state, we need to read and apply all the order’s events in the sequence.
|Event Id||Order Id||Event Type||Other Columns|
For all the benefits of Event Sourcing there is huge price to pay. To get current state of an entity we need to reapply all events. It may be OK if we need to do so for a single entity, but how about search queries that span through all entities. For example, we would like to know what are the top 10 orders with the highest total amount within the past month. To satisfy this query we need to reconstruct state of all Order entities, which is extremely slow… How do we solve this problem? Before we can answer, let’s look at CQRS.
CQRS stands for Command Query Responsibility Segregation. Might sound intimidating, but in reality, the concept boils down to separating reads and writes.
Consider classic model below where reads and writes are done from the same database. We have one model for storing and retrieving data, therefore we can’t optimize for both reads and writes at the same time. For example, creating multiple indices to speed up read queries slows down writes; and vice versa, removing indices improves writes but slows down reads. Of course, it may be possible to find equilibrium between reads and writes but very unlikely to achieve maximum performance for both.
With CQRS pattern read and write models are separated, therefore we do not need to balance performance between reads and writes, but can rather highly optimize for each. Different solutions can be used as storage for read and write models, for example relational or NoSQL database for writes and in-memory cache with relational database for reads.
CQRS solves the problem of read and writes optimization, but there is always price to pay. With CQRS the price is eventual consistency, when data is not guaranteed to be available for reads immediately after it was written, though eventually it will be. Certain applications can tolerate eventual consistency, but for the other it is significant impediment.
Event Sourcing ❤️ CQRS
Now we know each pattern enough to realize how both can play together. When we combine Event Sourcing and CQRS, domain events become our write model. We store all domain events in Event Store that is highly optimized for writes.
To implement reads of a single entity we have few options. The simplest to implement is to read events of a single entity from Event Store and replay them in order. Since each entity may not have too many events, performance should still be fine.
To implement queries, we can rely on read model. How do we create read model? Read model listens to domain events and applies events to each entity current state. New computed state is then persisted. Queries are executed against read model where latest state of each entity is already reconstructed.
One upside of combining CQRS and Event Sourcing is that we can have multiple read models serving different purposes. For example Query Model, Reporting Model and etc.
Hope this post demystified Event Sourcing and CQRS for you. If you still have questions, feel free to post it below.