Event Upcasting
Event upcasting transforms old event versions into the current shape at read time, letting event-sourced systems evolve their event schema without rewriting stored history.
Definition
Upcasting is the migration mechanism for event stores: when an event schema changes, an upcaster function converts stored old-version events into the current version as they are read. History stays immutable; interpretation adapts. Chained upcasters version by version migrate events across multiple generations of schema.
Context and scope
Standard practice in event-sourced systems and event streams with long retention. Complements schema registries with compatibility rules — upcasting handles what backward-compatible evolution cannot.
Common misconceptions
- Schema registries make upcasting unnecessary — registries police compatible changes; breaking changes — renaming, restructuring — still need upcasters
- Upcasters run when events are written — they run on read (or on replay), never mutating the store; writes stay append-only
- Every schema change needs one upcaster — only breaking changes do; additive optional fields usually project without transformation
Anti-patterns
- Writing a migration script that rewrites historical events, destroying the audit property event stores exist to provide
- Upcasting with lossy transformations and silently dropping data from old events
- Versioning events by timestamp instead of explicit schema version and guessing the shape
Related concepts
Related examples
Authoritative source
What it is
Event stores are immutable by contract, but businesses change their minds about what events should look like. Upcasting reconciles the two: stored facts stay frozen, and readers translate them into today vocabulary.
How it works
Each event carries an explicit schema version. A registry maps version pairs to upcaster functions. On read — or during projection rebuilds — the chain transforms v1 to v2 to current. Upcasters must be pure, total, and deterministic: same input, same output, forever, or replays diverge.
When to write them
Breaking changes: renamed fields, split or merged concepts, changed enum semantics, restructured payloads. Additive changes with defaults can skip transformation entirely.
Pitfalls
Lossy upcasts permanently reinterpret history — prefer additive shaping (old field plus new synthesized field). Untested upcasters corrupt every projection on the next rebuild; replay tests across synthetic historical events are mandatory. Chained upcasters multiply in maintenance cost — occasionally consolidate by writing one v1-to-current upcaster and retiring the chain.