Managing Event Schema and Versioning

1. Event Schema Evolution Pattern

ChangeCompatible?Strategy
Add optional fieldBackward compatibleDefault value for old consumers
Remove fieldForward compatible onlyKeep field, mark deprecated
Rename fieldBreakingAdd new, keep old, dual-write
Change typeBreakingNew event version
Add required fieldBreakingMust be optional or new version

2. Event Versioning Pattern

ApproachDetail
Field-Level VersioningAdd new fields without breaking old consumers
Schema VersioningEmbed schemaVersion in payload
Topic VersioningNew topic name (orders.v2) for breaking changes
Type-EmbeddedEvent type carries version: OrderPlaced.v2

3. Schema Registry Pattern

FeatureDetail
Centralized CatalogAll schemas (Avro/Protobuf/JSON Schema) stored centrally
Compatibility CheckEnforce backward/forward/full compatibility on register
Schema ID in MessageProducer prefixes schema ID; consumer fetches schema by ID
ToolsConfluent Schema Registry, Apicurio, AWS Glue Schema Registry

4. Tolerant Reader Pattern

PrincipleDetail
Ignore Unknown FieldsDon't fail on extra fields
Optional DefaultsTreat missing fields as default
RobustnessSurvive producer schema additions without redeploy

Example: Jackson Tolerant Reader

ObjectMapper mapper = new ObjectMapper()
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
OrderEvent evt = mapper.readValue(json, OrderEvent.class);

5. Expand and Contract Pattern

PhaseAction
1. ExpandProducer emits both old and new fields/formats
2. MigrateConsumers updated to read new format
3. ContractProducer drops old format once all consumers migrated

6. Upcaster Pattern

AspectDetail
PurposeTransform old event versions to current on read
WhereInside event store / consumer deserializer
BenefitDomain code only handles latest schema
ToolsAxon Upcaster, custom in deserializer

Example: Upcaster

// v1: { "amount": 100 }  → v2: { "amount": 100, "currency": "USD" }
public class OrderPlacedV1ToV2 implements Upcaster {
    public JsonNode upcast(JsonNode v1) {
        ((ObjectNode) v1).put("currency", "USD");
        return v1;
    }
}

7. Event Wrapper Pattern

LayerContains
Wrapper (metadata)id, type, version, occurredAt, correlationId, source
PayloadDomain-specific event body
BenefitGeneric processing (routing, dedupe, tracing) by metadata

8. Canonical Data Model Pattern

AspectDetail
DefinitionSingle org-wide model for shared concepts (Customer, Product)
ProsReduces N×N translations; consistent vocabulary
ConsBecomes lowest-common-denominator; high coordination cost
Modern TakePrefer per-context models + ACLs; canonical only for inter-org
Warning: Org-wide canonical models often become bottlenecks. Use sparingly and only for truly universal concepts.

9. Event Envelope Pattern

FieldPurpose
eventIdUnique ID for dedup
eventTypeDiscriminator (e.g., OrderPlaced)
eventVersionSchema version
aggregateIdSubject of event
occurredAtTimestamp
correlationIdRequest trace ID
causationIdID of event that caused this one
payloadDomain data

Example: CloudEvents Envelope

{
  "specversion": "1.0",
  "id": "evt_8af1",
  "type": "com.shop.order.placed.v2",
  "source": "/order-service",
  "time": "2026-05-15T10:00:00Z",
  "subject": "order/ord_123",
  "datacontenttype": "application/json",
  "data": { "orderId": "ord_123", "total": 99.50, "currency": "USD" }
}

10. Schema Validation Pattern

StageValidation
Producer-SideValidate before publish; fail fast in tests/CI
Broker-SideSchema registry rejects incompatible
Consumer-SideValidate on consume; reject malformed → DLQ
Contract TestsPact/CDC tests verify producer-consumer compatibility