Event Sourcing and CQRS
Concept
Two distinct patterns that are frequently paired but solve different problems:
- CQRS (Command Query Responsibility Segregation) — separates the model used to write data (commands) from the model used to read it (queries), instead of using one shared model for both. The write model can be normalized and validation-heavy; the read model can be a denormalized, pre-joined shape optimized purely for the queries the UI actually needs.
- Event Sourcing — instead of storing only the current state of an entity, the system stores the full history of state-changing events (
OrderPlaced,OrderShipped,OrderCancelled) as the source of truth. Current state is derived by replaying events, not read directly from a row that gets overwritten on every update.
They're often used together — the event store becomes the write side of a CQRS split, and one or more read-optimized "projections" are built by replaying events into whatever shape each query needs — but CQRS doesn't require events (you can split read/write models over a plain relational database), and event sourcing doesn't require CQRS (you could rebuild current state from events without a separate read model).
Tradeoffs
| Aspect | Cost | Benefit |
|---|---|---|
| CQRS | Two models to keep in sync (or accept eventual consistency between them) | Read model can be shaped and scaled entirely independently of write constraints |
| Event Sourcing | Every state transition needs an explicit event; replaying a long history to get current state gets slower over time without snapshotting | Full audit trail for free; can answer "what did this look like at time T" and rebuild any read model retroactively |
| Combined | Meaningfully more moving parts (event store, projections, eventual consistency between them) than a CRUD table | Fits domains where audit history and flexible reporting shapes both matter |
The shared cost across both is added complexity for a benefit (independent read scaling, full history) that a large fraction of systems simply don't need — this is one of the most commonly over-applied pattern pairs in the industry, adopted for its architectural appeal rather than a genuine requirement.
When to use / when not to
- Use when the domain has a genuine audit/compliance requirement to reconstruct history (financial ledgers, order lifecycles, regulatory trails) — the append-only event log is a natural fit for "prove what happened and when."
- Use CQRS alone (without event sourcing) when read and write load patterns diverge sharply — heavy write validation but very different, high-volume read shapes — without needing full history replay.
- Don't reach for either as a default for a standard CRUD service. If "what changed and when" was never a real requirement and read/write patterns aren't meaningfully different, a normal table with an
updated_atcolumn is simpler, cheaper, and easier for the next engineer to reason about. - Don't adopt event sourcing purely for the audit trail if a simpler append-only audit log table alongside a normal CRUD model would satisfy the same compliance requirement without the replay/projection machinery.
Common pitfall
Underestimating eventual consistency between the write side (event store) and the read-side projections. A command that just succeeded may not yet be reflected in the read model the UI queries immediately after — a user submits an order and refreshes the order list before the projection catches up, sees nothing, and assumes the submission failed. Systems that adopt CQRS/event sourcing without designing for this lag (read-your-writes guarantees, optimistic UI updates, or accepting and communicating the staleness window) turn an architectural benefit into a confusing user-facing bug.
Engineering Lens
The Principal-level signal here is restraint, not familiarity with the pattern. Anyone can describe CQRS and event sourcing; the harder and more valuable skill is correctly identifying when a system's actual requirements (audit trail, divergent read/write shapes) justify the complexity versus when it's pattern-matching for its own sake. Being the voice in a design review that says "we don't actually need full event sourcing here, a normal table plus an audit log table gets us the same compliance answer for a fraction of the operational cost" is exactly the kind of judgment call that distinguishes senior architectural thinking from resume-driven design.