Skip to main content

Operations + evolution (how ES survives contact with time)

The “hard” part of event sourcing is not writing the first version. It’s keeping it healthy after 6 months of change.

1) Event schema evolution

Assume:

  • events live forever
  • your code changes weekly

Practical rules:

  • never “edit history” in production (don’t rewrite old events)
  • introduce new event types for breaking changes
  • use upcasters for small compatibility upgrades

2) Projection rebuilds (you will do this)

You will rebuild projections when:

  • a bug existed in projection logic
  • you add a new read model
  • you change read model storage

Your system must support:

  • rebuild in a separate environment
  • cutover to the rebuilt read model
  • backfilling + catching up (no missing events)

3) Backpressure and hotspots

Some streams will be “hot” (high write rate). Strategies:

  • keep aggregates small (bounded context discipline)
  • reduce chatter events (avoid “every click” events)
  • snapshot for big streams
  • split aggregates if you accidentally modeled a “God aggregate”

4) Observability (non-negotiable)

Minimum signals:

  • append failures (wrong expected version)
  • projection lag (how far behind is read model)
  • outbox queue depth (unpublished count)
  • poison event count (events failing deserialization/upcast)
  • rebuild duration and throughput

Correlation/causation ids matter:

  • correlationId = request trace across services
  • causationId = “what caused this event”

5) Security + privacy

Event stores are great audit logs… which means they can also be great liability.

Consider:

  • encryption at rest
  • field-level encryption for sensitive payloads (depending on store)
  • keeping PII out of events (store references, not raw values)
  • GDPR “right to be forgotten” strategies (often requires indirection)

No universal answer—this is domain/legal dependent.

6) Deployment and compatibility

Plan for mixed versions:

  • old service instances might still write old event schemas
  • new instances must read old events

This pushes you toward:

  • tolerant readers
  • explicit event versioning
  • backward compatible serialization

Next

Let’s map these ideas to real .NET tooling: event-native DBs and Orleans.

Next: Real event stores (KurrentDB/EventStoreDB + Orleans)