Event sourcing
Save an aggregate as the sequence of events that changed it, and rebuild state by replaying them.
key idea / coordinatorservice / good pathin progress / watch outproblem / failureneutral
The problem with traditional saving
A normal table keeps only the latest state, so history, auditing and event publishing all have to be added on.
What event sourcing does
Instead of the current state, store every state change as an event. The current state is rebuilt by replaying them.
Commands become events
An update is split into two parts: one decides what happened, the other applies it.
Publishing and speed
Saved events can be published reliably. Snapshots shorten long histories.
Details you must handle
Events live forever, so their shape will change. Duplicates will arrive.
Is it worth it?
Event sourcing gives history and reliable events, and costs a different way of coding.
Event-driven versus event-sourced
Many systems are event-driven without being event-sourced. The difference is where the truth lives.
Brokers matter too. A traditional broker such as RabbitMQ is built for destructive consuming: once a consumer acknowledges a message, it is removed. A durable log such as Kafka keeps messages after they are consumed, so other consumers can read them and existing ones can reprocess them. Replay needs a durable log.
From Practical Event-Driven Microservices Architecture (ch. 3 and 4).
The broker as the event store
With a durable broker you can skip the events table and keep the events only on the log. This removes the need to write two places and keep them equal. A read model then builds a query-friendly projection from the same stream.
The price is that a query for one entity is awkward. A log is not a database, so loading one aggregate's events may mean scanning a topic. Kafka Streams state stores can help. Business rules that need a query (for example, "only 25% of products may cost over 400") must read an eventually consistent view and can see stale data.
| Good fit | Poor fit |
|---|---|
| Complex query needs (with CQRS read models) | Simple, isolated services |
| Core domain; new projections wanted later | Small data, strong consistency, rare change |
| Strong audit requirements | Teams not ready for snapshots and retention work |
A cousin is command sourcing: store the commands as received, not the events. It lets you replay user requests after fixing a bug or compare algorithm versions. It also adds async feedback problems, since a rejected command is discovered later. Most of the time, event sourcing is enough.
From Practical Event-Driven Microservices Architecture (ch. 4 and ch. 7).
Replay, compaction and forgetting
A compacted Kafka topic keeps only the latest message for each key. Publish one whole document per entity, keyed by id, and the topic works like a table that anyone can read from offset zero.
New consumers and rebuilds then need no special tool: the model is rebuilt by the same code that handles live events, and more instances can speed it up.
A permanent log clashes with the right to be forgotten. One answer is crypto shredding: encrypt each user's events with that user's key, and delete the key to make the data unreadable.
From Practical Event-Driven Microservices Architecture (ch. 3 and 4).
Joining streams into one event
Sometimes a consumer needs data from several streams, which have different keys and speeds. That makes ordering and concurrency hard. A process manager can join them into one denormalized event for that boundary.
From Practical Event-Driven Microservices Architecture (ch. 8, Denormalized Event Schema).
Recap
- Event sourcing saves an aggregate as a sequence of events.
- Rebuild state by replaying events. Use snapshots for long histories.
- process() creates events and apply() changes state.
- It publishes events reliably and keeps full history, but needs CQRS for queries.
- Event-driven is not event-sourced: in event sourcing the events are the entity, and replay needs a durable log.
- A compacted topic of documents keeps the latest per key; use crypto shredding for the right to be forgotten.
- A process manager can join several streams into one denormalized event.
Quick check
Show answer
It loads all of the order's events (or a snapshot plus later events), creates an empty Order and calls apply() for each event in order.