Skip to main content
Events are immutable facts. Each carries a type, payload, metadata, and monotonic position within the aggregate. Treat them like append-only logs rather than mutable rows.

Designing event types

  • Use verbs in past tense (person_registered, person_updated).
  • Keep payloads focused: one event should represent a single business fact.
  • Version types explicitly (invoice_paid.v2) if the meaning changes drastically; otherwise, evolve schemas through optional fields.

Schema evolution

Add optional fields first, then tighten them once you are confident they always appear. Use schemas to encode those rules:
For breaking changes, mint a new event type and deprecate the old one in code; schemas can lock a list of allowed events to enforce the transition.

Metadata for traceability

Include machine-readable routing hints and human context:
Plugins can filter on metadata without parsing payloads, and auditors can answer “who triggered this event?” instantly.

Replaying events

Use events to rebuild read models or repair downstream systems:
When replaying into plugins, set an @replay extension so receivers can differentiate new writes from backfills.

Idempotency

Events are naturally idempotent because they never overwrite prior entries. However, when events trigger external systems (webhooks, API calls), include deterministic identifiers (event_id, aggregate_id+version) so receivers deduplicate deliveries. Well-structured events keep the write path clean and make downstream integrations predictable.