Glossary + mental model
The one sentence
Event sourcing = your database contains facts (events), not the latest state.
You rebuild "current state" by replaying the facts.
The 5 objects you should picture
- Command: "Please do X" (intent). Might be rejected.
- Event: "X happened" (fact). Must be true forever.
- Aggregate: Runs business rules, turns commands into events.
- Event stream: Ordered list of events for one aggregate instance.
- Projection: A read-optimized view built from events (can be rebuilt).
"Events" are overloaded — don't confuse these
- Domain event (event sourcing): internal business fact, usually per aggregate stream.
- Integration event (distributed systems): something you publish to other services.
They can be related, but don't assume they are the same message or shape.
What ES gives you (when done right)
- Audit trail: You can answer "how did we get here?"
- Rebuildability: You can delete and rebuild read models safely.
- Concurrency that matches the model: streams + expected version.
- Temporal queries: "state at time T" by replaying until T (or snapshots).
What ES costs you (always)
- More moving parts: projection pipeline + schema evolution.
- Eventual consistency (if you have separate read models).
- You must design for idempotency in projections/integration.
- You must treat events as contracts (versioning/upcasters).
Stream mental model (critical)
For each aggregate instance, you'll have:
- stream id:
bank-account-{accountId} - events:
AccountOpened,MoneyDeposited, ... - stream version: 0..N (count of events, or last revision)
Appending is:
"Store these new events only if the stream is still at version N".
That's optimistic concurrency.
A tiny story (command → event)
Command:
Withdraw $50 from account 123.
Aggregate checks:
- account exists?
- not closed?
- balance - 50 >= 0?
If valid, emit event:
MoneyWithdrawn { amount: 50 }
If invalid, reject the command:
InsufficientFunds