Event Modelling Principles
Most system design approaches start with data structures or API contracts. Event modelling starts with what happens. That sounds like a small distinction, but after using it on several payments and fintech projects, I’m convinced it produces clearer designs with fewer miscommunications between engineers, product people, and domain experts.
Event modelling was created by Adam Dymitruk and builds on ideas from event sourcing, CQRS, and domain-driven design. It’s a visual design method, not a framework or a technology choice. You can use it whether you’re building with microservices, a monolith, or something in between.
The four building blocks
Every event model uses four types of elements.
Commands are intentions. A user or an external system wants something to happen. “Initiate a disbursement” is a command. Commands can be rejected, because they represent a request, not a fact. They carry the data needed to make a decision.
Events are facts. Once something has happened, it’s recorded as an event. “Disbursement initiated” is an event. Events are immutable: you don’t update or delete them. If something changes, you record a new event. The event log is your source of truth.
Read models (also called views) are projections built from events. They answer specific questions: “What’s the status of disbursement #4521?” or “How many disbursements are pending today?” A read model subscribes to events and maintains a queryable state optimised for a particular use case. Different consumers can build different read models from the same events.
Policies (sometimes called automations or reactors) are rules triggered by events. When event X happens, automatically issue command Y. “When a disbursement is initiated, run compliance checks” is a policy. Policies are where business rules live. They connect the pieces and encode workflow logic.
Worked example: processing a disbursement
Let’s walk through a concrete flow. A fintech platform needs to disburse funds to a merchant. Here’s how the event model breaks it down.
graph LR
C[Command:<br/>InitiateDisbursement] --> E1[Event:<br/>DisbursementInitiated]
E1 --> RM[Read Model:<br/>DisbursementStatus]
E1 --> P[Policy:<br/>RunComplianceCheck]
P --> C2[Command:<br/>ApproveDisbursement]
C2 --> E2[Event:<br/>DisbursementApproved]
E2 --> RM
E2 --> P2[Policy:<br/>ExecuteTransfer]
P2 --> C3[Command:<br/>TransferFunds]
C3 --> E3[Event:<br/>FundsTransferred]
E3 --> RM
style C fill:#4a9eff,color:#fff
style C2 fill:#4a9eff,color:#fff
style C3 fill:#4a9eff,color:#fff
style E1 fill:#ff9f43,color:#fff
style E2 fill:#ff9f43,color:#fff
style E3 fill:#ff9f43,color:#fff
style RM fill:#2ecc71,color:#fff
style P fill:#9b59b6,color:#fff
style P2 fill:#9b59b6,color:#fff
An operations user clicks “Disburse” in the dashboard. This issues an InitiateDisbursement command carrying the merchant ID, amount, currency, and reference. The command handler validates the inputs and checks that the merchant account exists. If validation passes, it records the event.
DisbursementInitiated is persisted with all relevant data: merchant, amount, currency, timestamp, and a correlation ID. This is now a fact in the system. It can’t be undone, only compensated by later events.
A DisbursementStatus read model subscribes to disbursement events. When it sees DisbursementInitiated, it creates an entry with status “pending”. The operations dashboard queries this read model to show the current state. When DisbursementApproved and FundsTransferred events arrive later, the read model updates the status accordingly. The dashboard user sees the disbursement move through its lifecycle without polling or complex joins.
A compliance policy listens for DisbursementInitiated events. For amounts above a threshold, it runs AML checks. If the checks pass, the policy issues an ApproveDisbursement command, which produces a DisbursementApproved event. Another policy, ExecuteTransfer, reacts to the approval by issuing a TransferFunds command to the payment processor. Each step is explicit and independently testable.
The point: every step is visible in the model. Product managers can trace the flow. Engineers can see which component owns which step. Nobody needs to reverse-engineer the logic from code.
When to use it
Event modelling works best when:
- The domain has clear state transitions. Payments, order processing, logistics, insurance claims, anything where entities move through defined states. If you can describe the process as “first X happens, then Y, then Z”, event modelling will clarify it.
- Multiple teams need a shared understanding. The visual model becomes a living spec. I’ve used it in workshops where engineers, product owners, and compliance officers all worked on the same board. Everyone could point to the part they cared about.
- You need an audit trail. Event-sourced systems get this for free. Every state change is an event, and the event log is the audit trail. In payments, this isn’t optional. Regulators expect it.
- The system has complex workflows. Policies make implicit business rules explicit. Instead of burying workflow logic in service code, you model it visibly.
When to skip it
Not everything needs event modelling. CRUD applications with simple data flows don’t benefit from the overhead. If your system is mostly “save this form to the database and display it”, you need a form handler and a database table, not an event model.
Similarly, if you’re prototyping and the domain isn’t well understood yet, event modelling can feel premature. You need enough domain knowledge to name meaningful events. “Something happened” isn’t useful. “DisbursementInitiated” is.
I’d also avoid retrofitting event modelling onto an existing system without buy-in. It’s a design tool, not a refactoring tool. Start with new features or bounded contexts.
So is it worth it?
Event modelling forces you to think in terms of what happens rather than what is stored. Once you make that switch, system boundaries and business rules get a lot easier to reason about.
The four building blocks (commands, events, read models, policies) are simple enough to sketch on a whiteboard in twenty minutes. The discipline is in naming events as past-tense facts and making every state transition visible. If you’re working in a domain with meaningful state changes, especially payments, compliance, or multi-step workflows, it’s worth learning.
Start with a single workflow. Model it with the four building blocks. If it clarifies things, expand from there. If it doesn’t, you’ve lost an hour.