Event Sourcing Pattern — Storing State as a Sequence of Events
Fast recall
Quick revision
Core answer
UPDATE accounts SET balance=100, you append MoneyDeposited(amount=100). You get a perfect audit log, time-travel debugging, free CQRS read models, and natural integration via event publication — at the cost of higher complexity, eventual-consistency reads, and the schema-evolution problem (events can never be deleted, so old event versions live forever). Use for financial / audit-critical / collaboration-heavy domains; avoid for simple CRUD.What an event store actually is, how state is rebuilt from events, snapshots for performance, schema evolution, and pairing with CQRS.
Event sourcing for CRUD systems where the audit log isn't valuable, or storing CRUD-style 'changed' events instead of meaningful business events.
Discuss event versioning strategies (upcasting, weak schema, copy-and-replace), snapshotting trade-offs, and how event sourcing pairs naturally with CQRS and DDD aggregates.
Interview-ready guide
Interview answer
Traditional vs event-sourced
Traditional: DB holds current state. UPDATE overwrites; previous value gone, reason for change gone. Audit log a separate afterthought.
Event-sourced: DB holds an append-only log of events. MoneyDeposited(100), MoneyWithdrawn(20). Current balance = sum of all events. Nothing lost. Full history of why the balance is what it is. State is a function: state = fold(events, initial).
How an event-sourced aggregate works
Account aggregate. Load: read all events for account-123, fromHistory replays them rebuilding state. Command: account.withdraw(20) — validates business rules, raises MoneyWithdrawn event, applies it (mutating in-memory). Save: append uncommitted events to the event store.
Crucially, business rules run in the aggregate; only events are persisted. Commands are not stored — only their outcomes.
The event store
Append-only log keyed by aggregate ID. Each aggregate has its own ordered stream. Optimistic concurrency — append checks expected stream version, conflicts on race. Subscribers (Kafka, outbox) project events into read models.
Implementations: EventStoreDB, Axon, Kafka with caveats, or Postgres with events(aggregate_id, version, type, payload, ts) table.
Snapshots — performance trick
Replaying 50k events on every load is slow. Snapshot every N events: stored separately, versioned with the aggregate. Load: latest snapshot → events since snapshot version → apply.
Snapshots are optimization, not source of truth. Always rebuildable.
Pairing with CQRS
Write side: load aggregate from events, execute command, emit + persist new events. Optimized for consistency and business rules.
Read side: subscribe to events, build denormalized read models tailored to query patterns. One model per UI need. Eventually consistent, usually milliseconds behind.
Why they pair: events are already there; building read models is just consuming them. CQRS without events needs separate change-data mechanism. Event sourcing without CQRS is awkward (querying event store directly is painful).
Schema evolution — the hard problem
Events immutable and live forever. Can never delete, rename a field, change semantics retroactively.
Three strategies:
- 1Weak schema, additive only: JSON/Protobuf. Add optional fields; never remove or rename. Old events still parse.
- 2Upcasting: on read, transform old event versions to latest shape. v1 → v2 in version-aware deserializers.
- 3Copy and replace (event migration): replay all events through transform, write new events to new stream, switch over. Heavy.
Best practice: model events as business facts in past tense (OrderPlaced, PaymentCaptured) — change schema slowly. Avoid CRUD-style events (CustomerUpdated{name, email}) — encode current shape, break on every schema change.
When to use
- Audit/compliance: banking, healthcare, insurance — regulator wants every change
- Time-travel debugging: state at 3pm yesterday is one fold away
- Complex domain with rich behavior
- Many interested parties: event-driven integration is free
- Need to replay business processes: bug found? Replay through fixed projector
When not to use
- Simple CRUD: audit log not valuable, complexity dwarfs benefit
- High-write, low-value data: telemetry, IoT — use time-series
- Reporting-heavy with thin business logic
- Team without DDD chops — requires modeling discipline
Common mistakes
- CRUD-as-events: emit business events, not state diffs
- Querying event store directly: build read models
- No snapshots for large aggregates: 100k events on every command crushes latency
- Mutating events in place: never; append a corrective event
- Event store as read store: mixed concerns
- No idempotent projectors: at-least-once delivery requires it
- Synchronous projection blocking commands
- Adopting because it sounds cool: without clear need, it's overhead
Recommendation
Use event sourcing for domains where the audit log, time-travel, replay, or rich event-driven integration genuinely matter — financial systems, regulated industries, complex collaborative domains. Pair with CQRS so reads stay fast and tailored. Model events as past-tense business facts, not CRUD diffs. Use snapshots once aggregates exceed a few hundred events. Plan schema evolution from day one (weak schema + upcasting). Don't adopt for simple CRUD; the operational and modeling tax is real and pays off only when the audit and replay capabilities are used.
Event Sourcing Pattern — Storing State as a Sequence of Events
Direct Answer
Event Sourcing stores every state change as an immutable, append-only event in an event store; the current state is derived by replaying events in order. Instead of UPDATE accounts SET balance=100, you append MoneyDeposited(amount=100). You get a perfect audit log, time-travel debugging, free CQRS read models, and natural integration via event publication — at the cost of higher complexity, eventual-consistency reads, and the schema-evolution problem (events can never be deleted, so old event versions live forever). Use for financial / audit-critical / collaboration-heavy domains; avoid for simple CRUD.
Common Mistakes & Pitfalls
Event sourcing for CRUD systems where the audit log isn't valuable, or storing CRUD-style 'changed' events instead of meaningful business events.
Check your understanding
Answer these without looking back at the article.
- How do you handle event schema changes once events are in production?
- Walk through loading an aggregate with snapshots — what's the lookup sequence?
- What happens if a projector falls behind by 1 hour — how do you catch up?
- When is event sourcing definitely the wrong choice?
Related interview prep categories
1Want a guided curriculum order?
These modules are also sequenced in our flagship backend interview track — open it when you prefer an ordered path instead of browsing by topic.