Muhammad S Tahir
Nov 5 · 6 min read

Event sourcing and CQRS: Match Made In Heaven — Part1

Software engineering is relatively new. Over the past few decades, engineers are pushing its limits by building more sophisticated, more reliable, and more scalable applications to fulfill the ever-increasing demands of the twenty-first century. Users have gone tech-savvy, and the amount of data we are producing these days is unbelievable. The way we build software to manage such demands in terms of reliability, security, and scalability have radically changed.

Traditionally software applications were developed using the CRUD style persistence model, mostly storing data in a Relational Database (RDBMS). This application style was very successful a decade ago but does not serve the need of applications these days. We need a more sophisticated application development model, Event Sourcing and CQRS is one much model which can help us achieve our scalability, reliability, and security needs.

CRUD Style

Lets first dissect the traditional model, understand its limitations so that we can better learn how Event Sourcing solves these challenges. Traditional software was mostly developed using the CRUD style development model. In this model, we define our domains and their relationships in a monolith application. These domain objects follow a lifecycle in which we create, update, read, and then finally delete these objects. These objects usually sleep in RDBMS tables in which their attributes are mapped to the columns of the corresponding database table. Let’s analyze some of the problems with this model.


Single Point of Failure:

As we know that traditional applications rely on RDBMS. We also know, based on our prior experience, that RDBMS is difficult to scale horizontally without sacrificing the consistency of the system. Therefore these databases are mostly scaled vertically. However, there is a hard limit on vertical scalability, and so this approach becomes a single point of failure for the entire applications. Microservices have solved this problem to a certain extent by dividing the domain into smaller manageable chunks and having a separate database for managing each domain. But still, when the application has a high demand for writes and reads, the database quickly becomes a bottleneck due to its inability to scale horizontally.


Non Traceability:

Traceability in traditional applications is achieved as a by product by either logging the information or by creating a history table in the database where all state changes are tracked. Both approaches have limitations and are not considered first-class citizens in the application domain. Logging sensitive information in the log files is an anti-pattern because logs have a much broader set of users ranging from developers, system admins, security auditors, etc. On the other hand, having another table to track the history of the domain state changes creates more burden on the already overloaded database and hence affects not only the stability and reliability of the entire platform but also creates a headache for developers in terms of evolving schema changes.


No Auditability:

As the traditional applications have no built-in traceability mechanism, therefore they can not offer any auditability, as both these concepts are closely related. Also, auditing not only requires what changed but also who changed it. To provide this feature, developers have to create custom logic in the applications.


No Scalability:

Previously I have mentioned that traditional monolithic applications mostly utilize RDBMS, which could be the single point of failure. RDBMS are designed to enforce ACID properties on all their records. To achieve this, they use a locking mechanism, so when the database row is being updated, it can not be read by others until the transaction is complete and the lock has been released. Locking mechanism creates a natural tension between the reads and writes, and hence the application suffers under heavy loads. Several techniques have been used in the past to address RDBMS scalability issues, but all of then have one limitation or another. Also, most of the commercial RDBMS solutions designed for scalability are extremely expensive to license and operate.


Mutable Domain:

As the domain objects participate in CRUD operations in traditional applications, therefore they are considered mutable. Any part of the application can fetch their current state from the database, mutate them in place, and then persist the updated state to the database. We all know that shared mutable state in a program is error-prone and difficult to manage, and protected by some locking mechanism. This use of locking mechanism again creates a scalability challenge within the application and is even more challenging to protect in distributed applications in which multiple instances are running across many machines.


Separation of Concerns:

We have explored previously that in traditional applications, reads and writes happen on the single RDBMS system. Some applications are read-heavy, whereas others generate huge write loads. Architecting an application that can handle both types is not only challenging but has its limitations in terms of scalability. However, if we are architecting a solution keeping in mind the principle of separation of concerns. Then we can architect the system — using CQRS concepts discussed later — which can withstand varying loads of reads and writes without affecting performance and is capable of achieving high scalability.


Request/Response or Synchronous Style:

Traditional applications were build using a request/response style, which is inherently synchronous. In this, the client sends the request and has to wait until the response is received successfully. Although this style is essential in certain use cases, but is difficult to scale because of its blocking nature. In the modern world of microservices, preference is on the asynchronous style of communication. In this style, the application performs its intended task and then generates an event that is published, and other applications can react to it.


Domain State Synchronization:

These days microservices architecture is a popular way of building applications. Microservices help us with designing decoupled systems incorporating single responsibility principles. These microservices often need to talk to each other, exchanging information over the network. A lot of companies are slowly moving their monolithic applications to microservices style. However, engineers are only focused on creating stand-alone services, each having their own databases and keeping the same request/response communication style. This model has challenges in a microservices world where other services may need the information to update their state. e.g., A person’s address can be stored by profile management microservice. However, there could be another microservice which manages Bank Account and also stores address information. This service needs to be notified each time the address changes. Profile management microservice is responsible for managing address data. There is no mechanism in RDBMS to emit the updates automatically for other services to consume; therefore developers have to write custom code for sending the updated address to all the clients which require the update. This approach becomes very challenging as no of clients increase. We will see how the event sourcing architecture can help solve this propagation problem.

Muhammad S Tahir

Written by

Highly accomplished visionary hands on CTO having extensive Blockchain, FinTech and AdTech experience with range of companies.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade