CQRS and Event Sourcing

Bingeek
5 min readNov 29, 2019

In this article, we are going to design an internal architecture of non-trivial microservice which is responsible for both managing its data state and exposing it.

Basically the microservice is responsible for creation and modification and also expose API that allows other services to query data.

CQRS

CQRS, stands for Command Query Responsibility Segregation, is an architectural pattern which separates reading and writing into two different sides.

  • Write side: a part that modifies the application state by executing commands.
  • Read side: a part that reads the application state by executing queries.

CQRS is derived from Command Query Separation (CQS) principle devised by Bertrand Mayer — author of Eiffel.

Basically, CQS principle is that there can be only two kind of methods on a class: the ones that mutate state and return void and the ones that return state but do not change it.

By applying CQRS, every method should be either a command (perform an action) or a query (return data).

Source: internet

For example, we have an OrderService to order some products

  • Without CQRS
  • With CQRS

Command

A command is an imperative message to request system to have some tasks performed. There names always use the indicative tense, like PlaceOrder. There is no content in the response, only queries are in charge of getting data.

Query

A query is a READ operation. It can be executed multiple times and will not affect the state of the system. The naming convention of the query is the same as the command’s

Event

An event is a message that serves as a notification for something that has already happened. Like a command, an event must respect a rule regarding names, it is always named with a past-participle verb, such as OrderPlaced

CQRS Pros and Cons

1. Pros:

Independent scaling: allows the read and write workloads to scale independently.

Security: easier to ensure that only the right domain entities are performing writes on the data.

Simpler queries: by storing a materialized view in the read database, the application can avoid complex joins when querying

Optimized data schema: the read side can use a schema that is optimized for queries, while the write side uses a schema that is optimized for updates.

2. Cons:

Complexity: include the Event Sourcing pattern will make it more complex

Messaging: it’s common to use messaging to process commands and publish update events. In that case, the application must handle message failures or duplicate messages.

How to connect the commands and the queries?

Mediator pattern is the answer. One of the implementation of Mediator in .Net Core is MediatR.

Let’s rock!

1. Install libraries by Package Manager Console or find it in Nuget

Install-Package MediatR
Install-Package MediatR.Extensions.Microsoft.DependencyInjection

2. Register MediatR in DI container in Startup.cs file, ConfigureServices method

services.AddMediatR(typeof(Startup));

3. Requests

Requests describe your commands and queries behavior.

IRequest interface

Query

Depend on each query, we can add properties (query parameters) into it

Command

DTO

All requests should implement IRequest interface. This interface is a contract which will be used by handler to process request.

4. Handlers

When request is created, you need a handler. The handler implements IRequestHandler interface with definition of input and output types

IRequestHandler interface

Each command or query has its own handler, and we will use Repository pattern including Unit of work

FindAllCatalogsQueryHandler class
AddCatalogCommandHandler class

Services shouldn’t trust the input from clients (empty data, invalid value, etc…), we need to validate it. One of the library that support us is Fluent validation. You can reference more here.

5. How to use command, query and handlers?

Event Sourcing

Event Sourcing is an approach for maintaining the state of business entities by recording each change of state as an event. It can be used without the CQRS pattern, but if we use CQRS, Event Sourcing fits well in conceptual terms. We cannot delete or modify events, you can only add more.

In Event Sourcing, every time something interesting happens, it is captured and stored as an event. All events are stored in Event Store: a database that supports the concept of Event Sourcing.

The example of events in an e-Commerce system are OrderCreated, PaymentDebited, OrderApproved, OrderRejected, …

In Event Sourcing architecture, when you publish one event from one microservice, other microservices can be reactive to those events and publish another set of events.

Why Event Sourcing?

  • Event Sourcing provides the sequence of events that led to the current state. Whether it is for audit or to perform machine learning on past data, the full sequence of events provides more insight than only retaining the current state.
  • Event store decouples different microservices. When emitting an event, an application does not need to know about other applications interested in the event

Let’s check the implementation

We’re going to use Marten library for event sourcing. You can see my previous post (part 4) about Marten for some basic information and preparation.

We’re going to create some simple event sourcing for Catalog service, to keep track of order

1. Create events

2. Make simple function to test (note that these codes below just for testing to see how event sourcing work)

After running this function, you can see the generated event sourcing tables with these data

The events are stored in the mt_events table, with these columns:

  • seg_id: a sequential identifier that acts as the primary key
  • id: a Guid value uniquely identifying the event across databases
  • stream_id: a foreign key to the event stream that contains the event
  • version: a numerical version of the event’s position within its event stream
  • data: the actual event data stored as JSONB
  • type: a string identifier for the event type that’s derived from the event type name. For example, events of type OrderAdded would be identified as order_added.
  • timestamp: a database timestamp written by the database when events are committed.
  • tenant_id: identifies the tenancy of the event
  • mt_dotnet_type: the full name of the underlying event type, including assembly name

You can get the full source codes in Github

Part 6: API gateways with Ocelot

--

--