A Command Bus Solution for CQRS and Event Sourcing Patterns: kediatR

A Command Bus Solution for CQRS and Event Sourcing Patterns: kediatR

A quick kediatR command bus solution guide for CQRS and Event Sourcing patterns

Understanding the problem

First, let’s start with a microservice architecture which is developed by using a database per service pattern.

Initially, it seems okay, but consider the following situations:

1- If it is desired to separate the write and read databases according to certain needs. (for example: writing PostgreSQL and reading from ElasticSearch)

2- If a pessimistic lock is used while writing to the database, read operations will be blocked until that lock is removed. Likewise, there may be lock situations where read operations block write operations.

3- Even if it varies according to the database, the indexes created for performing a more efficient read, may directly affect the write performance.

4- What if the app is read-intensive rather than a write-intensive one? It may be desirable to scale the read database separately.

What is CQRS(Command Query Responsibility Segregation) Pattern?

The main purpose of CQRS is to separate write operations from read operations. It is basically divided into two different types:

  • Commands: They change the state of the object or system. They include operations such as Insert, Update, Delete, and other operations that update records. Commands should be actions like “create a new product” or “update the basket”.
  • Queries: They don’t change the state of the object or system. They include operations such as Get, Search, and other operations that do not update any records. Queries shouldn’t modify the database like “get a product from the basket” or “get the translation of category”.

If the example given above is updated using the CQRS pattern:

Everything looks good. As seen, commands access the write database and queries access only the read database.

Of course, there are situations that need to be considered while applying this pattern:

1- It is necessary to keep the write and read databases in sync. But not in a synchronous way, it should be asynchronous because of performance requirements, write(command) operation should not wait until the read database is synced.

2- Due to eventual consistency between write and read databases, in some cases, the situation in the write database may not be in the same state as in the read database.

3- It is the developer’s responsibility to send queries to the read database and commands to the write database.

What is the Event Sourcing pattern?

This pattern, which is built on the CQRS pattern, prevents the documents in the write and read databases in different states.

Instead of going to a row-based structure as usual in the write database, every command operation is recorded in the database as an event. The write database can also be called Event Store. These events are immutable.

Not only the read database but also other systems or services can make updates on their own system by listening to these events.

As can be seen, the write database does not keep the final version of any document, it keeps the events of the updates made on the documents.

The projector creates the last state of the document by listening to the events in the write database and saves it to the read database.

As it will be noticed, write performance will be quite good in such a system. Instead of saving documents directly, events are recorded immutable, there is no updating process. In addition, there is retrospective audit data, in case of any error, the document in the Read DB can be recreated again by listening to all events from Write DB again (with the problematic event discarded). Therefore, there is a chance to take a step back in case of any error.

The most important thing to consider when applying this pattern is that after a certain time, the size of the Write DB(event store) will increase considerably. There are various solutions to solve this situation, for example, saving the state of the document after a certain time and deleting all events before that state.

What is kediatR?

We learned CQRS and Event Sourcing patterns, so how can we easily create these commands and queries in the code? kediatR is developed at Trendyol and it comes into our lives for that purpose.

kediatR provides us a command bus solution, by using this command bus we can execute our queries and commands easily. It supports synchronous and asynchronous (using kotlin coroutines) ways of command and query handling with native Kotlin implementation.

Currently, kediatR supports Spring Boot, Quarkus, Koin(can be used in Ktor) frameworks. And also both Kotlin and Java can be used.

There are four different types of kediatR, I will try to give examples using Spring Boot via Kotlin.

If you need additional information about Kotlin, you can check out this article.

Note that the command bus will be automatically injected into your code in run time and you don’t need to do anything.

Command: Equivalent to command in CQRS, returns no results.

Query: Equivalent to query in CQRS, returns results.

Notification: It works in a similar structure to Command, different meanings can be loaded according to your domain.

CommandWithResult: It works in a similar structure to Command, but this type of command returns results.

As you can see, creating commands and queries, and accessing their handlers from the command bus of kediatR is very easy.

There is also a pipeline solution of kediatR. You can implement custom logic before or after the command or query execution and in the case if they raise an exception.

There is also an IntelliJ Idea plugin for kediatR by using that you can create command and query handlers easily by clicking the cat, and you can find the handler of the command or query with one click.

How do we use kediatR in Product International Team?

As the Product International team, we mainly develop our application with Kotlin. We also use Spring Boot, Quarkus, and Ktor frameworks in our projects. The indispensable dependency of all our Kotlin projects is definitely kediatR.

The basic architecture of International Content API with CQRS pattern:

We use Couchbase as the write database and Elasticsearch as the read database. And syncing(replication) between these two databases is performed by Couchbase Elasticsearch Connector. We use kediatR to implement the CQRS pattern in our projects.

Another use case of kediatR is Kafka and RabbitMQ message handling. As I mentioned before you can use Notification type regarding your logic, we use Notification type for handling domain events. Every Kafka and RabbitMQ message actually is a Notification for our system, and we write this notification handler to handle Kafka and RabbitMQ events.

You do not necessarily need to separate write and read databases or use CQRS and Event Sourcing patterns to use KediatR, you can create a command query system using KediatR with a single database, even just to separate write and read logics and achieve single responsibility, it is also named as CQS Pattern.

Conclusion

When the needs are met, applying CQRS or Event Sourcing patterns by separating the read and write databases will help you scale your application and achieve single responsibility. But they are not a silver bullet, be careful when using them, make sure it meets your needs. On the other hand, kediatR offers a command bus solution to implement CQRS and Event Sourcing Patterns easily.

Great thanks to Bilal Kılıç, Caner Patır.

All feedbacks are welcome, thank you.

Source code: https://github.com/Trendyol/kediatR