Event Sourcing & CQRS · Evolution and Compliance
Event Versioning and Upcasting
Change event schemas without rewriting history.
Events are immutable, but your understanding evolves. Strategies: weak schema (tolerant readers ignore unknown fields, defaults for missing ones), new event types for new meaning (OrderShippedV2), and upcasting — transforming old event versions into the current shape at read time. Avoid rewriting stored events except in rare, controlled migrations (copy-and-transform into a new stream/store).
Versioning strategies
| Strategy | When | Cost |
|---|---|---|
| Tolerant reader / defaults | Additive fields | Low |
| Upcasting | Shape changes | Medium (code per version) |
| New event type | Different meaning | Consumers handle both |
| Copy-and-transform migration | Major redesign | High; rare |
def upcast(event: dict) -> dict:
if event["type"] == "CustomerRegistered" and event.get("schema", 1) == 1:
first, _, last = event["data"].pop("name").partition(" ") # v1 had a single name field
event["data"].update(first_name=first, last_name=last)
event["schema"] = 2
if event["type"] == "CustomerRegistered" and event["schema"] == 2:
event["data"].setdefault("marketing_consent", False) # v3 added consent
event["schema"] = 3
return event
# read path: events = [upcast(e) for e in raw_events]
// Single-event upcaster: OrderPlaced v1 (amount as double) → v2 (amount in cents)
public IntermediateEventRepresentation upcast(IntermediateEventRepresentation e) {
return e.upcastPayload(new SimpleSerializedType(OrderPlaced.class.getName(), "2"), JsonNode.class, node -> {
ObjectNode o = (ObjectNode) node;
o.put("amountCents", Math.round(o.remove("amount").asDouble() * 100));
return o;
});
}
Common pitfalls
- Editing events in place.
- Breaking changes in integration events consumed by other teams.
- Upcasters with side effects.
Key points
- Events are immutable
- Upcast old versions at read time
- New types for new meaning
Further reading: Greg Young: Versioning in an Event Sourced System
Practice questions
What is upcasting?
Answer
Transforming older versions of stored events into the current schema when reading them, so application code only handles the latest version.
Why avoid modifying stored events?
Answer
Events are the historical record; changing them breaks auditability, may invalidate projections and is risky to do correctly across large stores.
Open in the app to mark lessons complete, take notes and track your progress.