Domain Event
Explanation
Section titled “Explanation”A domain event is a record of something that already happened in the business domain. Other parts of the same domain are meant to learn about it and react (f4). It captures things that can trigger a change in the state of the application (f1).
Naming and immutability. The event happened in the past, so its name uses a past-tense verb, such as “order shipped”. The event must not change after it is created (f5). Events also naturally carry a point in time and a type (f2).
Source data. An event can hold the unchanging data of the outside trigger. This data stays separate from the changeable data used while processing it (f2).
Reacting inside one domain. Domain events spread state changes and side effects across several aggregates in the same domain model (f7). An aggregate is a cluster of objects treated as one unit; see the article Aggregate.
Raising events. An aggregate adds an event to a list on itself when something happens. The system delivers the events only after the transaction commits. This keeps raising an event separate from handling it (f8).
Eventual consistency. One recommended approach is that an aggregate method publishes an event to asynchronous subscribers. Other aggregates then catch up over time instead of instantly (f10).
Crossing context boundaries. Integration events mean almost the same as domain events but are built differently. They carry committed changes to other microservices, bounded contexts, or external applications, so they must be asynchronous (f6). A domain event handler can create an integration event and send it to an event bus. This is how a domain event leads to communication between bounded contexts (f9). See the article Bounded Context.
Link to event sourcing. Streams of events keep a record of all system inputs. The domain event pattern is necessary for event sourcing and supports audit (f3).
Example
Section titled “Example”An online shop has an order aggregate. When the customer pays, the order raises an “order paid” event with the order number and the time. After the transaction commits, a handler in the stock area reacts and reserves items. Another handler turns the event into an integration event for a separate shipping service. The order code does not know about either handler.
Trade-offs
Section titled “Trade-offs”Delivering events after the commit and asynchronously keeps the raising code decoupled from its reactions (f8). The price is eventual consistency: other aggregates are updated later, not at the same moment (f10). Domain events and integration events need different implementations, so teams must keep them apart (f6).
Diagram source
flowchart LR A[Order aggregate] -->|raises| E((Order paid event)) E -->|after commit| H1[Handler: reserve stock] E -->|after commit| H2[Handler: create integration event] H2 -->|event bus, asynchronous| B[Shipping bounded context]Checks
Section titled “Checks”- Domain events are named with past-tense verbs and are immutable.
- Domain events used inside one domain model are distinguished from integration events that cross bounded contexts.
- Integration events sent to other bounded contexts are asynchronous.
- Aggregates raise events, and dispatch is deferred until the transaction commits.
Related
Section titled “Related”- Aggregate
- Bounded Context
- Domain Model
- Event Sourcing with Distributed Event Log
- Entity Life Cycle
- Ubiquitous Language
- Domain-Driven Design
- Synchronous vs. Asynchronous Communication
Sources
Section titled “Sources”- Domain events: Design and implementation - .NET | Microsoft Learn — learn.microsoft.com, accessed 2026-11-09
- Domain Event — martinfowler.com, accessed 2026-11-09