Design Patterns Tutorial 0/70 lessons ~6 min read Lesson 49

    Event Sourcing

    Event Sourcing persists state as a sequence of state-changing events — not current row snapshot.

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Event Sourcing persists state as a sequence of state-changing events — not current row snapshot. Reconstruct any point in time by replaying events. Current state is a fold over history. Audit, debugging, and temporal queries become first-class.

    The story

    A bank audit asked: "show every state change for account #12345 in 2023." With Event Sourcing — one query against the event log. Competitors with snapshot DBs spent months reconstructing history from scattered logs.

    The business problem

    Snapshot-only storage loses history and blocks temporal queries:

    • Audit failure: can't prove who changed what when.
    • Debug blind: production bug needs past state reconstruction.
    • Temporal queries: "balance on March 1" impossible from current row.
    • Analytics gap: analysts want event stream, not just latest state.

    The problem teams faced

    Event Sourcing fits when:

    • Complete audit trail is regulatory or business requirement.
    • Temporal queries and replay are core features.
    • Debugging requires reconstructing historical state.
    • Event-driven analytics and projections are first-class consumers.

    Understanding the topic

    Intent: Store state changes as events; rebuild state by replay.

    • Event store — append-only log of domain events.
    • Aggregate — applies events to rebuild state; emits new events on command.
    • Snapshot — optional periodic state cache to speed replay.
    • Projection — read models built from event stream (pairs with CQRS).

    Internal architecture

    Event-sourced account aggregate:

    ts
    class Account {
    private balance = 0
    private changes: DomainEvent[] = []
    static fromHistory(events: DomainEvent[]) {
    const a = new Account()
    events.forEach(e => a.apply(e, record: false))
    return a
    }
    deposit(amount: Money) {
    this.apply(new MoneyDeposited(amount), record: true)
    }
    private apply(event: DomainEvent, record: boolean) {
    if (event instanceof MoneyDeposited) this.balance += event.amount
    if (record) this.changes.push(event)
    }
    uncommittedEvents() { return this.changes }
    }

    Visual explanation

    Three diagrams cover event log, replay, and vs snapshot DB:

    Event log append
    Deposit $100
    Event 1
    Withdraw $30
    Event 2
    Deposit $50
    Event 3
    Balance = fold
    Replay
    State is derivative — event log is source of truth.
    Audit query
    Account #12345
    Stream ID
    Filter 2023
    Time range
    Full history
    One query
    vs months rebuild
    Snapshot DB
    Bank audit story — event log answers in one query.
    Snapshot optimization
    10K events?
    Slow replay
    Snapshot at N
    Cached state
    Replay from snapshot
    Fast rebuild
    Event log still truth
    Append-only
    Snapshots are performance optimization — not source of truth.

    Informative example

    Before / after — snapshot update to event append:

    ts
    // ❌ Snapshot only — history lost
    await db.query("UPDATE accounts SET balance = $1 WHERE id = $2", [newBalance, id])
    // ✅ Event sourced — append event, rebuild state
    const events = await eventStore.load(id)
    const account = Account.fromHistory(events)
    account.withdraw(amount) // produces MoneyWithdrawn event
    await eventStore.append(id, account.uncommittedEvents(), expectedVersion: events.length)
    // Audit: SELECT * FROM events WHERE stream_id = id AND ts BETWEEN ...

    Execution workflow

    1Event sourcing command flow
    1 / 5

    Load history

    Fetch events for aggregate ID from event store.

    Or load snapshot + events since.

    Real-world use

    EventStoreDB, Axon Framework, banking ledger systems, Kafka as event log (with caveats), Marten (.NET), event-sourced DDD aggregates.

    Production case study

    Bank audit trail:

    • Requirement: complete 2023 state change history per account.
    • Event Sourced: single stream query — minutes.
    • Competitors: months reconstructing from partial logs.

    Trade-offs

    • Pro: perfect audit; temporal queries; natural event-driven projections.
    • Con: complexity; event schema evolution; replay performance without snapshots.
    • Con: not every domain needs full history — YAGNI cost.

    Decision framework

    • Use when audit/temporal/replay are core requirements.
    • Use with CQRS for read projections from event stream.
    • Avoid when simple CRUD with no history need — massive overhead.
    • Plan event versioning/upcasting from day one.

    Best practices

    • Events immutable — never edit; compensating events for corrections.
    • Optimistic concurrency on stream version — detect conflicts.
    • Snapshots for long streams — replay performance.
    • Outbox for reliable downstream publish after append.

    Anti-patterns to avoid

    • Event sourcing entire app including simple config tables.
    • Mutable events or deleting from log — breaks audit.
    • No upcasting strategy — schema change breaks replay.

    Common mistakes

    • Long replay without snapshots — slow aggregate load.
    • GDPR delete vs immutable log — needs tombstone/compaction strategy.

    Debugging tips

    • Replay events in test to reproduce production aggregate state exactly.
    • Log event type + version on append conflicts.

    Optimization strategies

    • Snapshot every N events or daily for hot aggregates.

    Common misconceptions

    • Event Sourcing ≠ Event-Driven. ES stores events as truth; event-driven is communication style.
    • Not all tables event-sourced — only aggregates where history matters.

    Advanced interview questions

    Interview Prep

    Practice concise answers, then expand each card for the explanation.

    4 questions
    1AdvancedQuestionEvent Sourcing vs audit log table?+

    Answer

    ES: events ARE state — rebuild by replay. Audit log: supplementary record; source of truth still snapshot.

    Follow-up

    Which for bank?
    2AdvancedQuestionEvent schema change?+

    Answer

    Upcasting: transform old events to new schema on read. Or versioned event types. Never mutate stored events.

    Follow-up

    Upcaster example?
    3AdvancedQuestionDelete user under GDPR?+

    Answer

    Hard with immutable log — crypto-shred, anonymize events, or compaction policies. Design retention upfront.

    Follow-up

    Tombstone events?
    4IntermediateQuestionSnapshot purpose?+

    Answer

    Performance — avoid replaying 10K events on every load. Event log remains source of truth.

    Follow-up

    Snapshot frequency?

    Summary

    You can explain event append, replay, snapshots, and audit advantages. Tell the bank 2023 audit story — if you can do that, you own Event Sourcing.

    Ready to mark this lesson complete?Track your journey across the entire course.