Why CQRS Matters: Evolving Monoliths into Scalable Systems
This article walks through the evolution from a traditional monolithic architecture to a CQRS‑based design, explaining the motivations, trade‑offs, and concrete techniques such as read‑write separation, message‑driven commands, eventual consistency, and event sourcing for building more scalable and maintainable systems.
Traditional Monolith Architecture
In a typical monolith there is a single API server (often RESTful) backed by a relational database. The client and server agree on a DTO (Data Transfer Object) format, and both read and write operations go through this DTO. The backend converts the DTO into domain objects that contain business logic and are persisted in the database.
Read/write separation is achieved by having a write path that performs CUD (Create/Update/Delete) on the database and returns an Ack/Nak, while the read path simply fetches DTOs. This model, however, suffers from two major drawbacks:
"Anemia" or CRUD model: business logic is scattered, domain knowledge disappears, and the code talks about inserting records instead of expressing domain concepts like "purchase".
Scalability bottleneck: the relational database becomes a single point of contention for both reads and writes, limiting horizontal scaling.
Task‑Based Monolith Architecture
To address the above issues, the article introduces the concept of a domain and replaces the DTO on the write side with a message that carries both an action and its data. This makes the intent of the operation explicit (e.g., a "rename" command) and allows the backend to route the command to the appropriate domain handler.
Although the write side now uses messages, the read side still needs DTOs to render UI views. For example, when displaying a user profile, the read side must still assemble activity data that is not part of the command payload.
At this stage the system still suffers from scalability problems because the write path must also generate read views.
CQS (Command Query Separation)
CQS is introduced to solve the read/write separation pain point. The write side (command) persists domain objects and optionally emits events, while the read side (query) works with pre‑built DTOs stored in a dedicated read database. The write side is responsible for creating these read‑optimized DTOs, enabling the read side to be a thin layer that only performs pagination, sorting, and simple retrieval.
The write path now prepares read views, which raises the question of who should generate them. The article argues that the write side must handle this because it has the full context of the state change.
Achieving Eventual Consistency
Because the read database is populated asynchronously, consistency between write and read sides must be managed. The article lists three common approaches ordered by latency:
Background thread replication (e.g., Redis replicates data to replicas immediately after a write).
Message‑queue workers that asynchronously copy data after an event is published.
ETL (Extract‑Transform‑Load) pipelines that run on a schedule, potentially taking minutes to hours.
All approaches require a single source of truth and the ability to recover from failures during the conversion process.
Data Types and Event Sourcing
The article distinguishes two data types:
State : the current snapshot (e.g., account balance).
Event : an immutable record of a change (e.g., a transaction entry).
By storing events as messages on the write side, the system can rebuild any read view on demand, which is the essence of event sourcing. However, rebuilding from events each time can be costly, so a hybrid approach that stores both state and events is recommended.
Lifecycle of Data in a CQRS System
Data originates from the client as a command, is transformed into a domain object, persisted, and then projected into various read views stored in specialized read databases. Clients finally retrieve these views as DTOs.
Conclusion
Domain‑Driven Design (DDD) and CQRS are not silver bullets; each architectural evolution solves specific problems while introducing new trade‑offs, such as eventual consistency. Understanding the motivations, benefits, and drawbacks of each pattern enables architects to make informed decisions and build systems that can evolve gracefully.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
