My notes on the book Versioning in an Event Sourcing System, by Gregory Young.
Why can’t I update an event?
- Events in an event store are essentially immutable, thus editing should be a strict no-no.
- Mutating events affect their consumers (e.g. projections).
- The audit trail will be lost. The moment you allow a single edit of an event, maintaining a proper audit log becomes impossible.
Basic Type Based Versioning
- Additive changes are non-breaking, therefore they are to be preferred for versioning purposes.
- To do so, create a new event (e.g.
UserCreated_V2
), and update its consumers to consume it instead of the old one. - A new version of an event must be convertible from the old version of the event; otherwise, it is not a new version of the event, but a rather a new event.
- You should generally avoid versioning your system via types in this way, since it leads to the inevitable conclusion that all consumers must be updated to understand the schema before a producer is.
Weak Schema
- A Weak Schema (e.g. data stored as JSON) provides more flexibility when deserializing the events into code. This way,
- additions are simply handled by the mapping.
- Renaming and remove things are also not desirable, even when using a Weak Schema.
General Versioning Concerns
- Do not add business logic to the events. Enrich the event at the time of its creation, if needed.
- Sometimes it may be worth including a description field to describe what the business logic did (e.g. “tax based on 18% MwSt”).
- Do not call external services when projecting the data. Make the call at the time of the event creation and enrich the information returned from the call onto the event.
- Avoid creating dependencies between different projections. It will create problems when replaying the projections.
- Snapshots share the same versioning issues found in structural data, and are usually not worth the implementation effort.
- Avoid “And”, i.e. merging many events together, since it forces a temporal coupling between the two things that are
happening.
TicketPaidForAndIssued
would be better if split intoTicketPaidFor
andTicketIssued
.
Correcting Wrong Events
- You cannot edit an event, but you can revert it by emitting a compensating event. E.g. from accounting:
- You want to credit an account 1.000$.
- You credit the account 10.000$ instead.
- You debit the account for 10.000$ (compensating action).
- You credit the account 1.000$.
Copy and Replace
- Copy and Replace can be use to upgrade events to the latest version. The general idea is to read the events out of the old stream and write them to a new one.
- Copy-Replace is the nuclear-option of versioning.
- Changing stream boundaries (Join Stream and Split Stream) also share the same difficulties as Copy-Replace.
- The previous patterns can be avoided by deriving two aggregates built off the same stream.
- When writing, it is imperative that both aggregates use
ExpectedVersion
set to the last event they read. - The setting of
ExpectedVersion
and having two aggregates can lead to more optimistic concurrency problems at runtime. If there is a lot of contention between the two aggregates, it may be best to Split-Stream.
- When writing, it is imperative that both aggregates use
- Copy-Transform: instead of migrating data in the Event Store for the new version of the application, the old system will be migrated to the new system. Instead of versioning storage on each release, treat each release as an entire new piece of software and migrate the old system to it. This strategy is called BigFlip.