Designing CQRS Architecture
1. Understanding CQRS Pattern
| Aspect | Detail |
|---|---|
| Command | State-changing op (write) |
| Query | Read; returns data without side effects |
| Separate models | Optimize each independently |
| When to use | Asymmetric R/W, complex domain, audit needs |
| When NOT | Simple CRUD; adds complexity |
2. Designing Command Side
| Element | Detail |
|---|---|
| Command handler | Loads aggregate, validates, applies events |
| Validation | Domain invariants enforced |
| Persistence | Event store or normalized OLTP |
| Output | Domain events emitted to bus |
3. Designing Query Side
| Element | Detail |
|---|---|
| Read model store | Denormalized: ES, Redis, MV |
| Projector | Subscribes to events, updates read store |
| Read API | Direct query; no domain logic |
| Multiple read models | One per use case |
4. Designing Read Model Projections
Example: Projection updater (Java)
@EventHandler
public void on(OrderPlaced e) {
readDb.upsert("order_summary", Map.of(
"id", e.orderId(),
"customer", e.customerName(),
"total", e.total(),
"status", "PLACED",
"placed_at", e.occurredAt()
));
}
| Concern | Detail |
|---|---|
| Idempotent | Track processed event_id |
| Order | Per aggregate, in event order |
| Failure handling | Retry, DLQ, alert |
5. Designing Event Sourcing with CQRS
| Synergy | Detail |
|---|---|
| Source of truth | Event store |
| Projections | Many read models from same events |
| Time travel | Replay to any point |
| Rebuild | Trash + replay to fix bugs |
6. Designing Eventual Consistency Handling
| UX Strategy | Detail |
|---|---|
| Optimistic UI | Show change immediately, reconcile |
| Read after write to write side | Bypass read model briefly |
| Polling with version | Wait until projection catches up |
| Display lag SLA | "Updated ~5s ago" |
7. Designing Read Model Synchronization
| Mechanism | Detail |
|---|---|
| Push (events) | Projector subscribes to event stream |
| Pull (CDC) | Debezium streams DB changes |
| Lag monitoring | Alert if > SLA |
| Backpressure | Pause writes if projector falls behind |
8. Designing Command Validation
| Layer | Validation |
|---|---|
| Schema | Required fields, types |
| Authorization | Caller can perform op |
| Domain invariants | Inside aggregate |
| Cross-aggregate | Saga or process manager |
9. Designing Query Optimization
| Technique | Detail |
|---|---|
| Denormalize | Pre-join in projection |
| Specialized stores | ES for search, Redis for KV |
| Caching | Read model + cache |
| Index per query | Tailored to access pattern |
10. Designing Read Model Rebuilding
Rebuild Process
- Create new read model store/version
- Replay events from offset 0 into new store
- Catch up to live tail
- Switch reads to new store atomically
- Decommission old store
11. Designing CQRS with Event Store
| Element | Detail |
|---|---|
| Write | Append events to stream-per-aggregate |
| Concurrency | Optimistic via expected version |
| Read | Subscribe + project |
| Tools | EventStoreDB, Marten + Postgres, Axon |
12. Designing CQRS Scaling Strategy
| Side | Scaling |
|---|---|
| Command side | Shard by aggregate id |
| Query side | Read replicas, denormalized stores |
| Projector | Partitioned consumer group |
| Hot aggregate | Partition by sub-key |