Loading unicorns
Blog/System Design

A Practical Guide to CQRS and Event Sourcing

By Rabi Thapa|August 10, 2025

Table of contents

TL;DR

CQRS is a system design pattern that separates read and write operations for better scalability and performance. Event sourcing stores all changes as events, enabling system recovery and audit trails. Together, they create resilient, scalable architectures perfect for complex systems with high traffic demands.

Command Query Responsibility Segregation (CQRS) is a powerful architectural pattern designed to help large, complex systems scale efficiently and remain maintainable as they grow. By separating the responsibilities of commands (writes) and queries (reads), CQRS allows each side of your application to be optimized for its specific workload. When paired with event sourcing, this approach unlocks even greater flexibility, resilience, and scalability.

What is CQRS?

CQRS divides an application into two distinct sides:

  • *Command Side: Handles operations that change data—create, update, and delete actions.
  • *Query Side: Handles operations that only read data, optimized for fast and efficient querying.

In traditional architectures, a single database and data model handle both reads and writes. This works for small systems, but as traffic and data volume increase, the database can become a bottleneck. CQRS solves this by allowing separate models and even separate databases for reading and writing, so each can be scaled and optimized independently.

CQRS architecture diagram showing separation of command and query sides with API Gateway routing write operations to command handlers and read operations to optimized query databases

Why Use CQRS?

  • *Performance: Reads and writes no longer compete for the same resources.
  • *Scalability: Each side can be scaled based on its load.
  • *Optimized Data Models: Use normalized databases for writes and denormalized or NoSQL stores for reads.
  • *Clear Responsibility: Codebases are easier to reason about, as each part has a single responsibility.

Introducing Event Sourcing

Event sourcing fits naturally with CQRS. Instead of updating the database directly, every change to the system is captured as an event and stored in an append-only log (the event store). The current state of the system is then derived by replaying these events.

Key benefits of event sourcing include:

  • *Resilience: If the read database is corrupted or lost, it can be rebuilt by replaying events from the event store.
  • *Auditability: Every change is tracked as a discrete event, making it easy to trace how the system reached its current state.
  • *Flexibility: You can create new read models or migrate to new databases by replaying the event log.

How CQRS and Event Sourcing Work Together

  1. User Interaction: Users interact with the system through an API gateway.
  2. Command Processing: Write operations (POST, PUT, PATCH, DELETE) are routed to the command side, where a command handler validates and processes the request, then emits an event to the event store.
  3. Event Store: The event store records every change as an event. This serves as the source of truth for the system's state.
  4. Event Processing: Event processors (sometimes called projectors) listen for new events and update the read database accordingly.
  5. Query Processing: Read operations (GET) are routed to the query side, which fetches data from the optimized read database.
  6. Recovery and Rebuilding: If the read database is lost or needs to be rebuilt, all events can be replayed from the event store to restore the system's state.

When to Use CQRS and Event Sourcing

  • *Your application has complex business logic and high scalability requirements.
  • *Read and write workloads have different performance or consistency needs.
  • *You need a clear audit trail or the ability to rebuild state from scratch.
  • *Eventual consistency is acceptable for your use case.

These patterns add architectural complexity, so they are best suited for large, evolving systems rather than small or simple applications.

When NOT to Use CQRS and Event Sourcing

While CQRS and event sourcing offer powerful benefits, they're not always the right choice. These patterns introduce significant complexity and should be avoided in certain scenarios:

  • *Simple Applications: If your application has straightforward CRUD operations with minimal business logic, the added complexity isn't justified.
  • *Small Teams: These patterns require deep understanding and careful implementation. Small teams may struggle with the learning curve and maintenance overhead.
  • *Tight Consistency Requirements: If your business logic requires immediate consistency across all operations, eventual consistency may not be acceptable.
  • *Limited Resources: Event sourcing requires additional storage for event logs and processing power for event replay. Consider this if resources are constrained.
  • *Simple Reporting Needs: If you only need basic reports and queries, a traditional database with proper indexing may be sufficient.
  • *Rapid Prototyping: For MVP development or proof-of-concepts, the development overhead may slow down iteration cycles.
  • *Regulatory Compliance Issues: Some industries require data deletion capabilities that conflict with event sourcing's append-only nature.

Remember: start simple and evolve your architecture as your system's complexity and requirements grow. CQRS and event sourcing are powerful tools, but they should solve real problems, not create unnecessary ones.

Building CQRS + Event Sourcing on AWS

In this setup, API Gateway routes write requests (POST, PUT, PATCH, DELETE) to the Command Service ELB and read requests (GET) to the Query Service ELB.

On the command side, EC2 handlers process changes and store them as events in the Write DB. Once updated, an SNS topic notifies downstream services, pushing messages to an SQS queue. A Lambda function consumes these messages, denormalizes the data, and updates the Read DB for fast queries.

The query side's EC2 handlers then serve data from the Read DB, ensuring reads and writes are scaled and optimized independently. This event-driven flow keeps the read model in sync while maintaining a complete history of changes.

AWS CQRS and Event Sourcing architecture diagram showing API Gateway, Command Service ELB, Query Service ELB, EC2 handlers, Write DB for event storage, SNS notifications, SQS queue, Lambda for data denormalization, and Read DB for optimized queries

This guide should give you a solid foundation for understanding and implementing CQRS and event sourcing, and how to take advantage of cloud platforms like AWS to build robust, scalable systems.