← Concepts

阅读中文版本

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.

architecture

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

https://martinfowler.com/eaaDev/EventSourcing.html

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.