Domain Events & Event Design

An event is a record that something meaningful happened: OrderPlaced, PaymentFailed, SubscriptionRenewed. In domain-driven design, domain events capture facts the business cares about, in the business's own language. In event-driven architectures, events are how services learn about each other's changes without tight coupling.

The quality of an event-driven system depends heavily on event design: what events exist, what they're called, what data they carry, and what metadata surrounds them. Poorly designed events (vague EntityUpdated messages, commands disguised as events, payloads that leak internal models) create coupling, ambiguity, and painful evolution. Well-designed events make systems understandable, and let new consumers appear without changing producers.

TL;DR

Quick Example

An integration event with an envelope and a fat payload:

Raising domain events from an aggregate (TypeScript):

Core Concepts

Events vs Commands vs Queries

Disguised commands like SendWelcomeEmailEvent couple the producer to a specific consumer's behavior. Publish CustomerRegistered, and let the email service decide to send a welcome email.

Domain Events vs Integration Events

Naming and Granularity

Payload Styles

Many teams favor reasonably fat integration events containing what consumers commonly need, which lets services work when the producer is down. See microservices data management.

Metadata

Standard metadata makes events traceable and processable:

CloudEvents standardizes much of this envelope.

Ordering and Delivery

Brokers generally guarantee ordering only within a partition, so key events by aggregate ID. Consumers must handle duplicates (at-least-once delivery) and occasionally out-of-order events across aggregates. Aggregate versions let consumers ignore stale updates. Reliable publishing uses the outbox pattern.

Publishing From Aggregates

In DDD, aggregates record domain events as part of state changes, and the application layer dispatches them after (or atomically with) persistence. Dispatch within the transaction only for handlers in the same consistency boundary. Cross-boundary handlers should react asynchronously, via the outbox and broker.

Best Practices

Discover Events With Domain Experts

Event storming workshops map a business process as a timeline of domain events with commands, actors, and policies. It's a fast way to find the right events and bounded contexts.

Publish Facts, Not Instructions

Producers describe what happened, and consumers own their reactions. This keeps producers stable as new consumers appear.

Keep Personal Data Minimal

Events are copied widely and retained long. Include only the necessary personal data, or use references and encryption to support deletion obligations.

Document Your Event Catalog

Maintain a catalog of events (AsyncAPI specs, EventCatalog, or registry docs) with owners, schemas, examples, and consumers. Discoverability prevents duplicate or conflicting events.

Common Mistakes

CRUD Events for Everything

OrderCreated, OrderUpdated, and OrderDeleted force consumers to diff payloads to infer what happened. Model meaningful transitions instead.

Exposing Internal Models

Serializing the internal ORM entity as the event payload couples every consumer to your database schema. Design explicit event contracts.

Relying on Global Ordering

Assuming all events across all aggregates arrive in order breaks under partitioning and retries. Design consumers for per-key ordering and idempotency.

FAQ

What is a domain event?

A record of something significant that happened in the business domain, named in the past tense using the domain's language (for example OrderShipped). Domain events capture business facts, and let other parts of the system react without the originating code knowing about them.

What's the difference between an event and a command?

A command is a request for an action directed at a specific handler, and it can be rejected. An event announces that something has already happened, and it's broadcast to any interested subscribers, so it can't be rejected, only reacted to.

Should events contain full data or just IDs?

It depends on consumer needs and coupling trade-offs. Thin events with IDs keep payloads small, but force consumers to call back to the producer. Fat events carry the relevant state, so consumers work independently. Many systems use moderately fat integration events for autonomy.

How should I name events?

Use past-tense verbs with business meaning, scoped to the domain: PaymentAuthorized, SubscriptionCancelled, ParcelDelivered. Avoid technical or generic names like DataChanged or RecordUpdated, and never name events as instructions.

Related Topics

References