Ask sales, support, finance, and engineering when an order becomes cancelled and the answers may sound compatible until they share a wall.
Sales says cancellation happens when the customer asks. Support says it
happens after a case is approved. Finance says a paid order remains active
until the refund clears. Engineering points to a cancelled_at column written
before the payment provider responds.
That disagreement is not workshop noise. It is the model.
EventStorming is useful because it lets people build a chronological account of the business together and challenge it while the contradictions are still cheap. It is not a reliable way to derive microservices by drawing boxes around clusters of orange notes.
Begin with facts in business language
The official EventStorming description calls it a flexible workshop format for collaborative exploration of complex domains. Its formats serve different scopes; a Big Picture session is not the same exercise as software design.
For an order-cancellation process, begin with things domain participants say have happened:
Cancellation requested
Cancellation approved
Refund requested
Refund declined
Refund completed
Inventory released
Order cancelledWrite events in the past tense and avoid implementation names. “Set
cancelled_at” describes a database mutation. “Order cancelled” claims
something about the business that finance and support can dispute.
Place the events in rough time order. Parallel paths and duplicates are useful at this stage. If two groups use the same words for different moments, keep both notes and mark the ambiguity instead of resolving it silently.
The first pass should make participation cheap. EventStorming’s incremental-notation pattern recommends introducing notation as the workshop needs it rather than front-loading a lecture. People cannot contribute domain knowledge while decoding a complete modelling grammar.
Add causes only after the timeline can be challenged
Once the event flow is visible, ask what decision or external action caused each event:
Customer ──▶ Request cancellation ──▶ Cancellation requested
Support agent ──▶ Approve cancellation ──▶ Cancellation approved
Cancellation approved + Captured payment
──▶ Request refund ──▶ Refund requestedThis exposes missing rules. Can a warehouse operator approve cancellation after picking begins? Does a declined refund restore the order, create a debt, or open a manual case? Is inventory released before or after refund success?
Mark those questions as hot spots. A hot spot is not a failure to finish the diagram. It records uncertainty that code currently has to resolve somehow.
Now add policies where one event leads to a decision:
Cancellation approved
──▶ when payment was captured, request refund
──▶ when payment was not captured, cancel orderThe conditional phrase is more valuable than the arrow. It names the state or rule the system needs to know.
Read the wall for language and consistency boundaries
Repeated terms reveal where language is stable and where it changes. Support may call the process a cancellation while finance calls its part a refund. Forcing one word across both groups can erase an important distinction.
Candidate boundaries appear where:
- the same term changes meaning;
- different rules and owners govern different parts of the flow;
- consistency requirements change; or
- one part of the process can progress while another is unavailable.
These are hypotheses, not generated bounded contexts. The cancellation and refund steps might belong to separate models, or they might remain modules in one application. Team ownership, transaction needs, deployment constraints, and expected change still decide the physical boundary.
Drawing a line around “Refund requested” and naming it PaymentService does
not prove that a network boundary helps. A modular monolith can preserve the
language boundary without accepting distributed delivery and observability
costs.
Move the boundary hypothesis into a context map
The wall shows a process over time. A context map records a different view: which model owns each meaning and how the models currently relate. For this example, a first map might be no more elaborate than:
Order Management
── Refund requested ──▶ Payments
◀─ Refund completed / Refund declined ──
── Inventory release requested ──▶ FulfilmentThe arrows are not message-broker configuration. Annotate each one with the
business meaning, the context that defines it, the side responsible for
translation, the consistency expectation, and any unresolved hot spot. If
support’s “cancelled” and finance’s “refunded” remain different facts, the map
should preserve that difference rather than rename both to Completed.
Map the current relationship before drawing the desired architecture. A shared table, a synchronous call, and a manually copied CSV are all relevant relationships even when nobody wants them in the target design. Omitting the awkward integration makes the map aspirational at exactly the point where it should make coupling visible.
The context map is still a hypothesis. It says where language and ownership appear to change; it does not decide whether those contexts become modules, services, or separately owned systems.
Test a boundary before paying for a service
For the cancellation/refund hypothesis, the cheapest useful experiment can
stay inside one deployment. Put cancellation and refund policy in separate
modules, translate RefundRequested at their boundary, and run the disputed
examples against that seam.
Then make the decision falsifiable:
- keep the boundary when cancellation and refund rules change independently, use different language, and can tolerate separate progress;
- redraw it when most changes cross both models or one invariant must commit across them; and
- introduce a network only when independent deployment or ownership is worth the delivery, failure, and observability obligations.
This experiment produces evidence that moving sticky notes cannot. It also allows the team to reject a weak boundary while the cost is still a module and a test, not a distributed migration.
Turn one disputed path into executable examples
A workshop artifact decays when it remains only a photograph. Select a rule that changed the team’s understanding and write examples while the people who challenged it are present:
Scenario: captured payment requires a completed refund
Given an order with a captured payment
When support approves its cancellation
Then a refund is requested
And the order is not cancelled yet
Scenario: unpaid order can be cancelled immediately
Given an order without a captured payment
When support approves its cancellation
Then the order is cancelled
And an inventory release is requestedThese examples do not dictate classes or event infrastructure. They preserve the discovered business distinction. The EventStorming site describes extracting acceptance tests as a way to capture residual ambiguity after process or software-design sessions.
An implementation might model the transition like this:
final class Order
{
public function approveCancellation(
?PaymentId $capturedPaymentId,
): array {
if ($capturedPaymentId !== null) {
return [new RefundRequested($this->id, $capturedPaymentId)];
}
$this->status = OrderStatus::Cancelled;
return [
new OrderCancelled($this->id),
new InventoryReleaseRequested($this->id),
];
}
}That code is only one design candidate. PaymentId is a translated fact at the
boundary, not a Payments aggregate reached through Order. The workshop
evidence is the rule that a captured payment delays final cancellation until
refund completion. A test should protect that rule even if the implementation
later stops using domain events.
Facilitation quality changes the evidence
A board produced by two architects while everyone else watches is not collaborative discovery. The official one person, one marker pattern exists because the person holding the marker becomes the model’s bottleneck.
A facilitator should:
- invite people who perform, decide, support, and observe the process;
- state the scope and stopping time;
- let participants create the first event timeline in parallel;
- ask for concrete counterexamples when a flow appears too clean;
- keep unresolved conflicts visible; and
- finish with owners and next actions for the important hot spots.
Remote tools make simultaneous writing possible but can hide disengagement. For a broad Big Picture exploration, EventStorming’s own resources recommend an in-person session when that is available. A smaller process-modelling session can work remotely when the facilitator deliberately manages participation.
The deliverable is a changed understanding
Do not measure the session by sticky-note count, bounded-context count, or how tidy the final photograph looks. A useful workshop changes at least one decision: it reveals a rule, separates two meanings, identifies an unknown, or produces an example the implementation can preserve.
The model should remain accessible, but it is provisional. Update durable language in code, tests, decision records, and service boundaries. Assign unresolved hot spots to people who can gather evidence.
EventStorming earns the room when it makes disagreement inspectable. If the team leaves with only an attractive diagram and no changed decisions, the workshop documented confidence rather than discovering the domain.