pattern

Event Upcasting

Transforming an old event's stored form into the current shape as it is read, so historical events remain replayable after the schema changes.

event-sourcingschema-evolutionversioning

The obligation event sourcing creates and that people do not anticipate: events are immutable and permanent, so an event written three years ago in a shape nobody uses any more must still be readable today, and forever.

You cannot rewrite history — that is the point of the store — so the transformation happens on read. An upcaster takes version 1 of OrderPlaced and returns version 2: filling a defaulted field, splitting one field into two, renaming, or converting a unit.

What this means in practice. The chain of upcasters grows monotonically and is never deleted, so it is code with a permanent maintenance obligation. Every upcaster needs a test with a real historical payload. And some changes cannot be expressed as an upcast at all, because the old event genuinely lacks the information — at which point the choice is a defaulted value with a documented meaning, or a new event type and a branch in the fold.

The mitigations worth adopting early: keep events small and factual, avoid embedding derived data that will need recomputing, and version explicitly from the first release rather than discovering you need versioning on the day you first need it.