Event Model
The business as a sequence of facts that happened, with the command that caused each and the read models and reactions it feeds.
flowchart LR
subgraph t1[" "]
direction TB
c1["cmd: Place Order"]
e1["OrderPlaced<br/><i>fact</i>"]
r1[("read: Order Status")]
c1 --> e1 --> r1
end
subgraph t2[" "]
direction TB
c2["cmd: Authorise Payment"]
e2["PaymentAuthorised<br/><i>fact</i>"]
r2[("read: Payment Ledger")]
c2 --> e2 --> r2
end
subgraph t3[" "]
direction TB
c3["policy: on PaymentAuthorised<br/>allocate stock"]
e3["StockAllocated<br/><i>fact</i>"]
r3[("read: Availability")]
c3 --> e3 --> r3
end
subgraph t4[" "]
direction TB
c4["policy: on StockAllocated<br/>request despatch"]
e4["ConsignmentCreated<br/><i>fact</i>"]
r4[("read: Tracking")]
c4 --> e4 --> r4
end
e1 --> c2
e2 --> c3
e3 --> c4What it is
Time runs left to right. Each column holds a command (an intention), the event it produced (a fact, always past tense), and the read model that fact updates. Policies are the reactions that turn one fact into the next command, and they are where the business rules actually live.
It comes out of an event storming workshop and is the bridge between that workshop's sticky notes and a design somebody can build.
When you produce it
Before choosing service boundaries in an event-driven system. The clusters of events that always move together are usually the boundaries, and they are often not the ones the org chart suggests.
Who reads it
The whole team, including domain experts — an event model that domain experts cannot read has failed at its main job, which is agreeing what happens.
What good looks like
- Events are past tense and business-meaningful.
OrderPlaced, notOrderTableUpdated. - Commands and events are visually distinct; conflating them hides where a request can be refused.
- Policies are named and shown as the trigger for the next command.
- Read models appear, so the query side is designed rather than discovered.
Common mistakes
- CRUD events.
CustomerUpdatedcarries no business meaning and forces every consumer to diff. - Modelling only the happy path, so compensation is never designed.
- Treating database change events as domain events. They are not; one is an implementation detail of a table, the other is a published contract.