Skip to content
API Design Learn/Event-Driven APIs
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Designing Domain Events

7 min read

You'll learn to

  • -Name and shape domain events so they describe what happened, not what should happen next
  • -Decide how much data to include in an event payload - a thin reference versus a full snapshot

An event-driven API communicates through a different mechanism than request-response: services publish events describing things that have already happened, and other services subscribe to react to them, fully decoupled from whoever produced the event. Designing the events themselves well is just as much an API design discipline as designing REST endpoints or a GraphQL schema.

Naming: Past Tense, Not Commands

An event describes something that already happened
{
  "event_type": "order.shipped",
  "event_id": "evt_7f3a2b1c",
  "occurred_at": "2026-08-07T14:30:00Z",
  "data": {
    "order_id": 501,
    "tracking_number": "1Z999AA10123456784",
    "shipped_at": "2026-08-07T14:30:00Z"
  }
}

Event names should be past-tense facts (`order.shipped`, `payment.completed`) rather than imperative commands (`ship_order`, `complete_payment`) - a command implies an instruction the receiver must obey, which couples the publisher to what subscribers should do. An event, by contrast, is just a statement of fact: "this happened," leaving each subscriber free to decide independently whether and how to react. This is the same decoupling principle behind the Observer pattern from LLD Fundamentals, applied at the scale of services instead of objects.

Payload Design: Thin Reference vs. Full Snapshot

Thin: just enough to identify what changed
{"event_type": "order.updated", "order_id": 501}
// subscribers that need details must call back to fetch the current order
Full snapshot: the complete relevant state at the moment of the event
{
  "event_type": "order.updated",
  "order_id": 501,
  "status": "shipped",
  "total": 149.99,
  "items": [{"product_id": 42, "quantity": 2}]
}
// subscribers get everything they likely need without an extra round trip

A thin event keeps the payload small and avoids ever shipping stale data (subscribers always fetch current state when they act), at the cost of an extra round-trip API call for every subscriber that needs details. A full-snapshot event avoids that round trip but risks the payload becoming stale by the time a subscriber processes it (especially under retry or delayed delivery), and it couples every subscriber to the exact shape of the snapshot the publisher chose to include. Neither is universally correct - the choice depends on how many subscribers typically need the detailed data and how tolerant they are of slightly-stale values.

An Event Catalog

As a system accumulates dozens of event types across many services, an explicit, documented event catalog - what events exist, their exact payload schema, who publishes each one - becomes as important as REST's resource documentation or a GraphQL schema's self-describing types. Without it, discovering "does an event already exist for this" becomes tribal knowledge, and teams end up publishing near-duplicate events because nobody could find the existing one.

Every event should carry a unique `event_id` (distinct from the business entity's own ID, like `order_id`) - this is what allows the exactly-once/idempotent-consumer patterns covered later in this module to detect and safely ignore a duplicate delivery of the same event.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.

Ready to Build This?

Design Event Storm in the API Design Lab's gRPC & Event-Driven act.

ScaleDojo Logo
Initializing ScaleDojo