Library
Domain-Driven Design Distilled · 6 of 10
Domain-Driven Design Distilled
AI Software Development HIGH

Domain Events and Event Sourcing

domain-events event-sourcing eventual-consistency commands causal-ordering

Key Principle

A Domain Event is a record of a business-significant occurrence, named as a past-tense verb (e.g., ProductCreated). The fundamental distinction: a Command can be rejected; a Domain Event cannot. Commands are requests (imperative: CreateProduct); events are facts (past tense: ProductCreated).

"A command may be rejected, but a Domain Event is a matter of history and cannot logically be denied." (Chapter 6)

Domain Events serve as the mechanism for Aggregate Rule 4: update other Aggregates using eventual consistency. When Aggregate A's state change must eventually affect Aggregate B, A publishes a Domain Event that B consumes.

Why This Matters

Without Domain Events, cross-Aggregate communication requires either: (1) modifying multiple Aggregates in one transaction (violating the single-Aggregate-per-transaction rule, causing contention), or (2) ad-hoc polling mechanisms (fragile, latent, hard to debug). Domain Events make the causal chain between business occurrences explicit and traceable.

Good Examples

Property design: Event properties derive from the command that caused them — include those properties, not the entire Aggregate state. Over-enriched events obscure what actually happened and force consumers to diff states. The event name and its properties should make the business occurrence self-evident. (Chapter 6)

Atomic persistence: "The modified Aggregate and the Domain Event [must] be saved together in the same transaction." (Chapter 6). If the Aggregate updates but the event isn't published (or vice versa), the system becomes inconsistent. This is a mechanical requirement, not a guideline.

Event Sourcing: Persist all Domain Events as the record of Aggregate state rather than persisting current state. Reconstitution = replaying the event stream. Business value: complete history at the individual-occurrence level for compliance, analytics, debugging, and needs not yet imagined. (Chapter 6)

Counterpoints

Causal ordering: Events saved in causal order may not arrive in causal order at distributed nodes. The consuming Bounded Context must enforce causality via sequence identifiers or causal identifiers — processing an effect before its cause produces corrupt state. (Chapter 6)

Event Enrichment vs. Query-Back: Over-enriching events risks security issues (exposing data to unauthorized consumers) and coupling. Query-Back (thin events, consumers query source for details) improves security but reduces consumer autonomy. Neither approach is universally correct. (Chapter 6)

CQRS obligation: Event Sourcing almost certainly requires CQRS because reading current state from an event stream is expensive without projections. The two patterns are coupled in practice, but CQRS is deferred to the companion volume [IDDD]. (Chapter 6)

Key Quotes

"A command may be rejected, but a Domain Event is a matter of history and cannot logically be denied." — Vaughn Vernon, Chapter 6

"The modified Aggregate and the Domain Event [must] be saved together in the same transaction." — Vaughn Vernon, Chapter 6

Rules of Thumb

  • Name events as past-tense verbs: ProductCreated, BacklogItemCommitted
  • Include command-derived properties, not entire Aggregate state
  • Always persist the Aggregate and its Domain Events atomically
  • Enforce causal ordering at the consumer, not just the producer
  • Avoid generically named events (e.g., BacklogItemUpdated) — be specific about what happened
  • Event Sourcing requires CQRS in practice

Related References