1. What is CQRS?
CQRS is an architectural pattern that separates read operations (queries) from write operations (commands) into different models or services.
The key idea:
Don’t use the same data model to update and read data.
2. Why Separate Reads and Writes?
In a typical CRUD system:
- The same model handles read and write operations.
- This means one data structure must satisfy both needs — often forcing compromises.
With CQRS:
- Commands (writes) → update the data store (create, update, delete).
- Queries (reads) → fetch from an optimized read model (could be a different DB, a denormalized table, or even a cache).
3. How It Works
┌─────────────┐
│ Client │
└─────┬───────┘
Command │ Query
│
┌─────────────┴───────────────┐
│ Application │
│ (Separate Paths for R/W) │
└───────────┬───────┬─────────┘
│ │
Write Model Read Model
(Commands) (Queries)
│ │
▼ ▼
DB-W DB-R
(Normalized) (Optimized / Cached)
4. Benefits
-
Optimized for each use case:
- Write side can be normalized, transactional.
- Read side can be denormalized for fast lookups.
- Scales independently: You can scale the read side (often much heavier) separately from the write side.
- Clearer design: Read and write logic are simpler when separated.
- Plays well with Event Sourcing: Write model produces events, read model subscribes and updates projections.
5. Drawbacks
- Complexity: Two models, more moving parts.
- Data consistency: Read model may be eventually consistent with write model.
- Operational overhead: More services/databases to maintain.
6. Example Scenario
E-commerce Orders
-
Write side:
- User places an order → validated → saved in relational DB.
-
Read side:
- Order history page → uses a denormalized table or Elasticsearch index for fast searching/filtering.
7. When to Use CQRS
✅ Useful when:
- You have high read-to-write ratio.
- Reads and writes have very different performance needs.
- You need to scale reads separately from writes.
- You’re building event-driven systems.
❌ Overkill when:
- Small/simple app with balanced reads/writes.
- No scalability issues yet.
In short: CQRS says — keep your write models clean for updates, and your read models optimized for queries. They don’t have to be the same structure, the same DB, or even the same service.
Now let’s connect CQRS with Event Sourcing, because they often complement each other in real-world systems.
1. Event Sourcing — Quick Recap
- Instead of storing just the current state in the DB, we store all events that happened.
- State = result of replaying events.
Example: Instead of saving:
Balance: $120
We store:
Deposited $100
Withdrew $30
Deposited $50
If we replay these events → balance = $120.
2. How Event Sourcing Fits with CQRS
With CQRS + Event Sourcing:
-
Write Side (Commands)
- User sends a command → system validates it → generates one or more events.
- These events are stored in the Event Store (append-only log).
-
Read Side (Queries)
- A projection service listens to events and updates read-optimized views (could be SQL, NoSQL, Elasticsearch, etc.).
- Queries hit these precomputed read models.
3. Architecture Flow
┌─────────────┐
│ Client │
└─────┬───────┘
Command │ Query
│
┌────────▼──────────┐
│ Write Model │ (Validates commands)
└────────┬──────────┘
│
Store Event(s)
│
┌───────▼─────────┐
│ Event Store │ (Append-only log)
└───────┬─────────┘
│
┌─────────────▼─────────────┐
│ Projections │ (Builds read models)
└─────────────┬─────────────┘
│
┌──────▼───────┐
│ Read Model │ (Optimized for queries)
└──────────────┘
4. Benefits of CQRS + Event Sourcing
- Auditability → Full history of changes.
- Rebuild capability → Can replay events to rebuild read models if schema changes.
- Independent Scaling → Reads and writes scale separately.
- Flexibility → Can build multiple read models from the same events (e.g., one for reporting, one for search).
5. Drawbacks
- Increased complexity → More components (event store, projection services, etc.).
- Event versioning → If event schema changes, you must handle backward compatibility.
- Eventual consistency → Read models might lag behind writes.
6. Real-World Example — Banking
-
Write Side:
- Commands:
DepositMoney,WithdrawMoney. - Events:
MoneyDeposited,MoneyWithdrawnstored in event store.
- Commands:
-
Read Side:
- Projection builds Account Balance View from events for fast querying.
- Another projection builds Transaction History View.
💡 Key takeaway:
- CQRS gives you separate read/write models.
- Event Sourcing gives you a history of events to feed those models.
- Together, they allow highly scalable, auditable, and flexible systems.