Topic 13.13
CQRS
In one line
Separate the read model from the write model — different schema, different store, different scaling.
Think of it like this
A restaurant with separate staff for taking orders (optimized for writing orders down quickly) and for the kitchen display screen showing all pending orders (optimized for a fast overview) — two different, specially-built views instead of one struggling to do both jobs well.
Key ideas
- 01
Principle: commands (writes) and queries (reads) get their OWN models instead of one model pretending to serve both.
- 02
Write side: canonical, normalized, transactions (source of truth).
- 03
Read side: denormalized projections shaped for queries (7.7) — often from events (13.12) and cached.
- 04
Scaling logic: reads scale horizontally trivially (read replicas + caches + projections); writes stay small/consistent.
- 05
Consistency between sides: eventual — event → projection lag is the freshness bound.
- 06
When NOT to use CQRS: simple CRUD where reads==writes shaped — YAGNI until a real read skew exists.
- 07
Interview: 'reads are 50:1 and dashboard-shaped — I give them a read model; the write path stays unchanged'.
Java / Spring map
- →
Command side: @Transactional services; read side: projections (JPA projections, ES index, Redis) fed by events.
Code & diagrams
Explain without notes
Give a system where CQRS pays for itself (different read skew) vs one where it's ceremony.
Practice
Split the notification dashboard: write model for campaigns + read model for analytics, with the sync path.
Trade-offs
- ↔
CQRS adds sync machinery + two models to keep honest; the payoff is read scaling + tailored read shapes.
Run it in production
You've designed it. Now build, operate, and break the same idea hands-on in the DevOps courses:
Completion checklist
I can justify CQRS with read skew and name the freshness bound of its projections.