Evolve persisted events
Persisted events are data contracts. A deployment may read events written months ago, and a rolling deployment may exchange messages between two application versions. Change event types with that lifetime in mind.
Motivation: Source code can be replaced in one deployment, but journal data remains. Treat an event change as a data-compatibility decision because recovery and rebuilds still execute against the old representation.
Register stable journal names
Without a stable mapping, a journal manifest can depend on the CLR assembly and type name. Register a name before the actor system writes events:
open FCQRS.FSharp
Fcqrs.journalTypes
[ journalType<Document.Event> "document.event"
journalType<User.Event> "user.event" ]
|
The mapping lets a type move to another namespace or assembly while old rows keep the same manifest. Update the mapping to point the stable name at the new CLR type.
Choose the change strategy
Use the smallest compatible strategy:
Change |
Strategy |
|---|---|
New business outcome |
Add a new event case |
New data not required for old history |
Add optional data or a new event version |
Field renamed in source only |
Keep its serialized name or provide compatibility decoding |
Field meaning changes |
Create a new event case or version |
Type moves namespace or assembly |
Keep the registered journal name |
Old event no longer affects current state |
Keep a fold case that applies its historical meaning |
Do not change an old event's meaning to match a new rule. Replay must reproduce the decision that was recorded at the time, not reinterpret history using today's rule.
Keep readers ahead of writers
For a rolling deployment:
- deploy code that can read both the old and new event shapes;
- verify recovery and projection replay on that version;
- deploy code that starts writing the new shape;
- remove old decoding only after no stored event or active node needs it.
If old and new nodes exchange commands or saga messages, both versions must understand every shape used during the overlap.
Add compatibility tests
Keep representative serialized events from the previous release as test fixtures. Verify that the new code can deserialize them and fold them into the expected state. Also replay a mixed history containing old and new event cases.
Test projections with the same mixed history. Aggregate recovery succeeding does not prove that every read model understands the new event.
Rebuild derived data
When an event remains valid but a projection interpretation changes, leave the journal alone. Deploy the corrected projection and rebuild its read model.
Stable journal names handle type identity. They do not automatically migrate event fields or meanings. Those changes remain part of the application's compatibility design.
FCQRS