Event sourcing: stop losing history, start using it
Event sourcing: what it is (and no, it's not just logging)
With event sourcing, events are the source of truth. You don't store the "final state" of an object, you store everything that happened to it. Then you rebuild the state by replaying those events. Yes, like a replay. And no, it's not "just logs": a log tells a story, an event is the truth.
Basic example: instead of saving balance = 120, you record MoneyDeposited(200) then MoneyWithdrawn(80). The balance is a result, not a magic number from nowhere.
Why would you bother?
Because future-you will thank you when:
- you must audit who did what, when, and why,
- you want to debug by replaying reality,
- you need to rebuild a model after business changes,
- you want projections (read models) without wrecking your database.
In short: you pay some complexity now, so you stop improvising post-mortems later.
The core building blocks (no smoke)
- Event: an immutable fact, in the past.
OrderPlaced, notPlaceOrder. - Stream: the sequence of events for one aggregate.
- Aggregate: the domain object that applies events to rebuild state.
- Projection: a derived view (SQL, Elastic, cache, etc.) for fast reads.
When to use it (and when to avoid it)
Use it if:
- your domain changes often,
- history is critical,
- you need rock-solid traceability.
Avoid it if:
- your CRUD is basic,
- you don't need audit trails,
- your team is still discovering repositories.
Tiny Symfony starter example
We'll keep it simple: a Cart aggregate, events, and an in-memory event store. No full CQRS, no Kafka, no wizardry. Just the core.
1) Define the event
php
2) The aggregate that replays events
php
3) A minimal in-memory event store
php
4) Usage inside a Symfony service
php
There. No ORM, no cache, no parade. Just the central idea: events are the truth, state is a computed view. You can plug projections and Symfony Messenger listeners later if you want to play with the big kids.
Takeaways (if you have to explain it)
- Event sourcing stores history, not just the result.
- You rebuild state by replaying events.
- It's perfect for audit and evolving domains, pointless for boring CRUD.
- Symfony won't do it for you, but it lets you structure it cleanly.
If this feels heavy, good: you're trading immediate comfort for long-term clarity. You pay the cost now, or future-you pays it later. Your call.