Skip to main content

↔️ CQRS

Separate read and write models

The Restaurant Analogy

In a restaurant:

SystemPurposeOptimized For
Kitchen (Write)Takes orders, cooks foodComplex operations, cooking
Menu (Read)Shows dishes, displays pricesSimple lookup, browsing

They're separate systems serving different needs!

CQRS separates reads from writes for different optimization.


What Is CQRS?

CQRS = Command Query Responsibility Segregation

ApproachModel
TraditionalOne model for reading AND writing
CQRSSeparate models, each optimized for its purpose
ModelHandlesExamples
Command modelWritesCreate, update, delete
Query modelReadsGet, list, search

Why Separate Reads and Writes?

Different Requirements

Writes (Commands)Reads (Queries)
Validate business rulesJust get data fast
Ensure consistencyMay need joins, aggregations
Handle complex logicDifferent shapes than writes
Need transactionsCan be eventually consistent

One Model Can't Optimize Both

Traditional ModelProblem
Normalized for writesSlow for complex queries (JOINs)
Denormalized for readsComplex and slow for writes

CQRS: Optimize each separately!


How It Works

Simple CQRS (Same Database)

       ┌─────────────┐
       │   Client    │
       └──────┬──────┘
              │
     ┌────────┴────────┐
     ↓                 ↓
┌─────────┐      ┌─────────┐
│ Command │      │  Query  │
│ Handler │      │ Handler │
└────┬────┘      └────┬────┘
     └────────┬───────┘
              ↓
        ┌──────────┐
        │ Database │
        └──────────┘

Same database, different code paths.

Full CQRS (Separate Databases)

       ┌─────────────┐
       │   Client    │
       └──────┬──────┘
              │
     ┌────────┴────────┐
     ↓                 ↓
┌─────────┐      ┌─────────┐
│ Command │      │  Query  │
│ Handler │      │ Handler │
└────┬────┘      └────┬────┘
     ↓                ↓
┌─────────┐ sync ┌─────────┐
│ Write DB│─────→│ Read DB │
└─────────┘      └─────────┘

Different databases, optimized for each purpose.

Commands and Queries

Commands

AspectDescription
PurposeIntent to change something
NamingVerbs: CreateOrder, CancelOrder, UpdateProfile
ReturnsSuccess/failure, maybe ID
Side effectsYes - modifies state

Queries

AspectDescription
PurposeRequest for data
NamingQuestions: GetOrder, ListOrders, FindByEmail
ReturnsData
Side effectsNone - pure read

Benefits

1. Independent Scaling

PatternScaling
100 reads for 1 writeScale read servers ×10
Reads don't compete with writesBetter performance

2. Optimized Models

Write ModelRead Model
NormalizedDenormalized
Strong consistencyPre-computed aggregates
Business rules enforcedFast queries

3. Different Storage

Write SideRead Side
PostgreSQL (relational, ACID)Elasticsearch (search-optimized)
Event storeRedis (cache)
TransactionalEventually consistent

Best tool for each job!


CQRS + Event Sourcing

Common pairing:

StepWhat Happens
1Command arrives
2Generates events (event sourcing)
3Events stored
4Events projected to read model

Benefits:

  • Complete audit trail
  • Multiple read models from same events
  • Time-travel debugging

Eventual Consistency

AspectDescription
What happensWrite occurs, read model updated asynchronously
DelayMilliseconds to seconds
Trade-offFast writes, slightly stale reads

Handling in UI

StrategyImplementation
Use returned dataAfter write, use response directly
Optimistic updateShow change immediately in UI
Processing message"Your changes are being saved..."
Read your writesRoute user's reads to primary

When to Use CQRS

Good Fit

ScenarioWhy CQRS Helps
Read-heavy applicationsScale reads independently
Complex queriesOptimize read model for queries
Different read/write requirementsDifferent models for each
Event sourcingNatural pairing
Multiple read viewsDifferent projections

Probably Overkill

ScenarioWhy Not
Simple CRUDToo much complexity
Read and write are similarNo benefit
Small scaleOverhead not worth it
Strong consistency everywhereEventual consistency won't work

Common Mistakes

1. Applying CQRS Everywhere

Not every entity needs CQRS. Apply where it adds value, keep simple parts simple.

2. Ignoring Consistency Needs

Some use cases need immediate consistency (user's own profile, account balance). Use "read your own writes" pattern.

3. Complex Sync Logic

Write DB → Read DB sync can be complex. Events and projections help, but it's more moving parts.


FAQ

Q: Do I need two databases?

No! Start with same database, separate code paths. Add separate storage later if needed.

Q: How are read models updated?

Events from commands → Project to read model. Or direct sync after write.

Q: What about transactions?

Write side has transactions. Eventual consistency to read side.

Q: CQRS without event sourcing?

Yes! CQRS is just separating reads/writes. Event sourcing is a separate pattern.


Summary

CQRS separates read and write operations into different models, optimizing each for its purpose.

Key Takeaways:

  • Commands: write operations (verbs)
  • Queries: read operations (questions)
  • Separate models, different optimization
  • Enables independent scaling
  • Often paired with event sourcing
  • Eventual consistency is a trade-off
  • Not for every application

CQRS: Different models for different jobs!

Leave a Comment

Comments (0)

Be the first to comment on this concept.

Comments are approved automatically.