|$ curl https://forge-ai.dev/api/markdown?path=docs/architecture/cqrs
$cat docs/cqrs-&-event-sourcing.md
updated Today·20-28 min read·published

CQRS & Event Sourcing

ArchitectureCQRSEvent SourcingAdvanced🎯Free Tools
Introduction

CQRS (Command Query Responsibility Segregation) separates the model used to change state (commands) from the model used to read state (queries). Event sourcing stores state as a sequence of domain events, rebuilding current state by replaying those events. They are independent patterns that often appear together — but CQRS does not require event sourcing, and event sourcing does not require microservices.

Both add cognitive and operational complexity. Justify them with requirements: divergent read/write shapes, auditability, replay, or extreme read scalability — not résumé-driven design.

CQRS Deep Dive

In a traditional CRUD model, the same relational schema serves writes and every screen. When read needs diverge wildly — dashboards, search, personalized feeds — the write model becomes compromised by read optimizations or reads become painfully joined.

Write side

Validates commands; enforces invariants; updates write store or appends events.

Read side

Projections optimized for queries; eventually updated from write-side changes.

Sync integration

Same process updates both — simpler, less isolation.

Async integration

Events update read models — scales independently; eventual consistency.

RequirementCQRS help
Reads and writes scale differentlyScale read replicas/projections independently
Many read shapes from one write modelMultiple projections
Simple isomorphic CRUDUsually unnecessary
Event Sourcing Overview

Instead of storing only the latest row state, store each change as an immutable event. Current state is a fold over events (often snapshotted). Benefits: complete audit trail, temporal queries, rebuild projections, debuggability of "how did we get here?"

  • Why: compliance audit, complex domain with time-travel needs, rebuildable read models.
  • How: append-only event store; aggregates apply events; projections subscribe; upcasters for evolution.
  • Cost: versioning events, GDPR erasure strategies, operational expertise, harder casual debugging without tools.
order-events.txt
TEXT
1OrderPlaced(orderId, items, total)
2PaymentCaptured(orderId, chargeId)
3OrderShipped(orderId, tracking)
4OrderCancelled(orderId, reason)
5
6Fold → current Order aggregate state
7Projection → orders_by_customer read table
Complexity Cost
CostManifestation
Cognitive loadMore moving parts; harder onboarding
Consistency UXReads lag writes
Schema evolutionEvent versioning/upcasting
Tooling needsAdmin viewers, reproject jobs
PrivacyDeleting PII from immutable logs

danger

Event sourcing + CQRS + microservices + choreography simultaneously is a complexity multiplier. Adopt incrementally where pain is proven.
When Requirements Justify
SignalConsider
Audit "who changed what when" is MustEvent sourcing or dense audit log
Read models need denormalization at scaleCQRS projections
Rebuild search index from historyEvent log as source
Team new to domain + CRUD fitsAvoid — modular monolith CRUD
How to Adopt Incrementally
  • Start with CQRS-lite: separate query services/SQL views without full event sourcing.
  • Introduce domain events for integration before storing all state as events.
  • If event sourcing a subdomain, keep it inside a clear boundary (e.g., payments ledger).
  • Build projection rebuild tooling on day one.
  • Document consistency and privacy strategies in ADRs.
command-handler.sketch.ts
TypeScript
1export async function cancelOrder(cmd: {{ orderId: string; reason: string }}, store: EventStore) {{
2 const history = await store.load(cmd.orderId);
3 const order = Order.fromEvents(history);
4 const events = order.cancel(cmd.reason); // domain decides
5 await store.append(cmd.orderId, events, history.version);
6}}
Common Mistakes
  • Event sourcing CRUD entities with no domain behavior — expensive audit log.
  • Huge events that embed entire DB rows every time.
  • No snapshot strategy — replays become slow.
  • Assuming CQRS means mandatory messaging everywhere.
  • Ignoring GDPR: plan for crypto-shredding or minimized payloads.
CQRS / ES Checklist
cqrs-checklist.txt
TEXT
1[ ] Divergent read/write requirements documented
2[ ] Consistency lag acceptable to product
3[ ] Privacy/retention strategy for events
4[ ] Projection rebuild tested
5[ ] Scope limited to subdomain with clear benefit
6[ ] Team skills / tooling ready
7[ ] ADR with alternatives (including "not now")
CQRS Variants
VariantDescription
CQRS-liteSeparate read DTOs/queries; same DB
CQRS + messagingAsync projections from events
CQRS + ESWrite model is event stream
PolarizedOnly hottest subdomain uses CQRS

Prefer the weakest variant that satisfies requirements. Most teams benefit from CQRS-lite long before full event sourcing.

Privacy & Event Stores

Immutable logs conflict with erasure rights. Strategies include crypto-shredding (delete encryption keys for personal data), storing PII outside the log, or minimized events with references to erasable stores. Decide before production data accumulates.

warning

"We will figure out GDPR later" on an event-sourced system is a compliance crisis in slow motion.
Example: Shipping Read Model
cqrs-shipping.txt
TEXT
1Write: ShipPackage command → PackageShipped event
2Read: customer tracking page uses denormalized tracking_view
3Read: ops board uses warehouse_workload_view
4Both projections consume the same events with different shapes
Operating CQRS/ES

You need projection lag metrics, rebuild jobs, event store backups, and admin tooling to inspect streams. Without these, the system is a research project. Budget platform time alongside feature work.

  • Alert when projection lag exceeds UX-agreed thresholds.
  • Practice rebuilding a read model in staging monthly.
  • Document how to handle partial projection failures.
  • Keep a runbook for "stuck consumer / poison event".
CQRS vs Well-Designed CRUD
NeedTry firstEscalate to CQRS/ES when
Faster readsIndexes, cache, replicasModels fundamentally diverge
Audit trailAudit table / temporal tablesFull rebuild & behavior replay needed
Complex reportingWarehouse ETLOnline serving needs same events

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.