CQRS — A simple yet elegant architectural design pattern

Aditya Rao
8 min readMar 6, 2021

CQRS (Command and Query Responsibility Segregation) — is a design pattern which separates your READ and WRITE/UPDATE/DELETE operations within your code.

In practice, CQRS is a very simple pattern that doesn’t require a lot of investment. It can be easily extended with more complex techniques like event-driven architecture, event-sourcing, or polyglot persistence. But they’re not always needed. Even without applying any extra patterns, CQRS can offer better decoupling, and code structure that is easier to understand.

TLDR — You can get a high level understanding by just looking at this image

Use-Case

Let is consider a basic CRUD Model for Orders in a restaurant and same business model — A Customer places order, rarely updates it or deletes it, but the same order is seen by the customer, the cashier, the cook\s and other people in between. This model is a READ Heavy model.

And this system is growing rapidly with new features, we would like to a menu, where each dish will have its ingredients, we also want to show the location where those ingredients are sourced from, we want to include the number of calories each dish has, so on and so forth.

This is a classic case of Data Driven system, where we have new entities nested inside each other, connected with one another, and lets add EVENTS to this mix — Such as OrderTaken, OrderCooked, OrderDeliverd, BillPaid and others.

On top of this we are expanding to new services, like customers can order online, they can ask for small changes in the ingredients — basically we are adding a ton of new business use case as we build the system

What is CQRS

The idea of CQRS is to divide the application into two different sections which work with different models:

  • The Create-Update-Delete COMMAND which is responsible for creating or mutating the data, and the QUERY which fetches the data.
  • It also has an Internal model it writes with, and one or many models using which we can read(more on this later)
CQRS Command and Queries with their won model

Commands

Commands are the thing that writes with a model. It is used to create a new entity or change the state of the entity.

  • A Command is an tasked based operation, rather than data centric.
  • A Command may be a Function call with an API.
  • A Command can be placed on a queue for async processing (Events, Tasks).

In a way a Command can change the state, is an entity created, can cause change in the system directly or indirectly. In API terminology it is defined as Create/Update/Delete : Entity

A Command is behavior-centric and not data-centric. It is about the intent to change something, it does not map to the format of a resource (like DTOs in an API). It can contain data that will help to process the intent but that’s it.

We also talk about Task Based systems and not Resource Based systems. The Writes part doesn’t accept new resources or patch of existing resources: they accept tasks aka Commands.

Query

Queries are methods that are used to read data — These operations do not change the data with this you can imagine, wherein we have multiple data models which we might combine for a the response of an API — Such as UserModel and OrderModel to show all Orders made by said User.

The advantages of this system comes into play when we start adding other components to our architecture

  • Adding cache
  • Faster queries ( We have can 3 DB’s to Read from and 1 DB to write)
  • Do a lot of precomputation on the backend — Helps keeping the client side lite.
  • High latency — there is a difference between Faster queries and this ;)

The Flow

The Command Flow, here the API Model which is used by the client can do two things,

  1. It can create an entity using, mapping and validating with the internal model
  2. It can trigger an Event, which is responsible to change the state of an entity.

The Query Flow, is pretty straight forward, you have an client side need, that is either —

  1. Is part of a single internal model, where in it is fetched directly.
  2. Or is a combination of the internal models, and there is some aggregation of models.

Advantages of CQRS

  • Consistency — It is far easier to process transactions with consistent data than to handle all of the edge cases that eventual consistency can bring into play. Most systems can be eventually consistent on the Query side.
  • Data Storage — The Command side being a transaction processor in a relational structure would want to store data in a normalized way, probably near 3rd Normal Form (3NF). The Query side would want data in a de-normalized way to minimize the number of joins needed to get a given set of data. In a relational structure likely in 1st Normal Form (1NF).
  • Independent Scaling — CQRS allows the read and write workloads to scale independently, and may result in fewer lock contentions.
  • Optimized data schemas — The read side can use a schema that is optimized for queries, while the write side uses a schema that is optimized for updates.
  • Security — It’s easier to ensure that only the right domain entities are performing writes on the data.
  • Separation of concerns — Segregating the read and write sides can result in models that are more maintainable and flexible. Most of the complex business logic goes into the write model. The read model can be relatively simple.
  • Simpler queries — By storing a materialized view in the read database, the application can avoid complex joins when querying.

When to use CQRS

Consider CQRS for the following scenarios:

  • Collaborative domains where many users access the same data in parallel. CQRS allows you to define commands with enough granularity to minimize merge conflicts at the domain level, and conflicts that do arise can be merged by the command.
  • Task-based user interfaces where users are guided through a complex process as a series of steps or with complex domain models. The write model has a full command-processing stack with business logic, input validation, and business validation. The write model may treat a set of associated objects as a single unit for data changes (an aggregate, in DDD terminology) and ensure that these objects are always in a consistent state. The read model has no business logic or validation stack, and just returns a DTO for use in a view model. The read model is eventually consistent with the write model.
  • Scenarios where performance of data reads must be fine tuned separately from performance of data writes, especially when the number of reads is much greater than the number of writes. In this scenario, you can scale out the read model, but run the write model on just a few instances. A small number of write model instances also helps to minimize the occurrence of merge conflicts.
  • Scenarios where one team of developers can focus on the complex domain model that is part of the write model, and another team can focus on the read model and the user interfaces.
  • Scenarios where the system is expected to evolve over time and might contain multiple versions of the model, or where business rules change regularly.
  • Integration with other systems, especially in combination with event sourcing, where the temporal failure of one subsystem shouldn’t affect the availability of the others.

This pattern isn’t recommended when:

  • The domain or the business rules are simple.
  • A simple CRUD-style user interface and data access operations are sufficient.

Consider applying CQRS to limited sections of your system where it will be most valuable.

Let’s see how it looks in Code

This part will not have “Code” but a a general low level design which helped me understand CQRS and implement it in my personal projects, and also in some of the projects that I have worked on — CQRS has really changed the way I write Microservices that are DDD.

Controller Orders
- GET/Orders?id
- GET/Orders
- POST/Orders
- PUT/Orders
- DELTE/Orders?id
Controller Restaurant
- GET/Restaurant
- GET/Restaurant?id
- POST/Restaurant
- PUT/Restaurant
- DELTE/Restaurant?id
Controller Dish
- GET/Dish
- GET/Dish?id
- POST/Dish
- PUT/Dish
- DELTE/Dish?id
User
- GET/User?id

Lets define the Query Models

Here we can see that, we are defining the abstraction of all the queries that will be getting served to the Client

Lets define the Command Models

Now the Interesting part, the handler — Lets take a look

Integration into an existing application.

CQRS is pretty flexible, here are some of my tips to adding CQRS and reaping its benefits

  1. Start with a single service or an API — this while not add performance gains but it will make the code more readable and easy to add new features and functionalities.
  2. Separate your databases — One set (I said set, becuase in scale you would have sharded your DB) which is mainly used for writing, and one set for reading. You can say this is kinda like infra level CQRS?
  3. Separate your services — A set of services that are only used to write into the database which are connected only with the “write set DB”, and the other to only read obviously connected with the “read set DB”, with this you have achieved a very level of separation. This structure is highly advantageous when you are using REST to write, update, delete and GraphQL to GET the data

Somethings that CQRS goes well with

  1. Caching — this goes well becuase, you have models to write which are connected with services — so its easy to perform cache invalidation. Yeah I’d say its easy to invalidate as a bigger pro than performance
  2. Event Sourcing — I will be writing about this soon

--

--