Reference · gazar.dev ← From Senior to Staff

Domain-driven design · discovery workshop

Event storming, walked one sticky at a time.

S8 said: find the boundary by watching the language. Event storming is the workshop that does it, fast, with the whole room and a wall of sticky notes. This sheet runs a real session on a real project, a food-delivery platform, and shows the board grow as you scroll: from a chaotic pile of events to clean bounded contexts you can turn into services. Companion to S8 · Domain-Driven Design.

00

First, the colour grammar of the wall

Every sticky has a meaning by its colour. This is the whole notation, there is nothing else to learn. Keep this legend in your eye; the rest of the page just adds these one colour at a time.

Order Placed

Domain event orange

Something that happened, in past tense. The spine of the whole board.

Place Order

Command blue

An intention that causes an event. Usually an actor's decision.

Order

Aggregate yellow

The thing a command lands on and that enforces the rules. Emits the event.

Customer

Actor small yellow

A person/role who issues a command. The who.

Payment Gateway

External system pink

A system outside your control that you call or that calls you.

whenever… then…

Policy lilac

A reactive rule: "whenever this event, then issue that command". The glue.

Incoming Orders

Read model green

The view an actor looks at to decide their next command.

HOTSPOT

Hotspot pink, rotated

A problem, a question, a disagreement. Park it loudly, don't solve it now.

The grammar in one line

an actor reads a read model, issues a command  →  a aggregate checks its rules and emits a domain event  →  a policy reacts ("whenever…") and fires the next command  (sometimes through an external system).

Read that sentence left to right and you can read any event-storming board on earth. Everything below is just this loop, drawn for one real domain, one layer at a time.

The project we'll storm: FoodHive

A food-delivery platform. A customer orders a meal from a restaurant; we charge them, the restaurant cooks it, a courier delivers it, and we settle the money. Simple to picture, but it spans four very different jobs (ordering, money, the kitchen, the road), which is exactly what makes it a good event-storming subject. We have the right people in the room:

In the room

A product manager, two engineers, someone from the restaurant-ops team, someone from payments, and a courier-operations lead. Mixed perspectives on purpose, the disagreements are the gold.

The scope sentence

"From a customer hitting checkout to the money being settled after delivery." One sentence on the wall keeps the storm from boiling the ocean.

1

Everyone, in silence · 20 min

Chaotic exploration: throw every event on the wall

Hand out orange stickies. Everyone writes domain events in past tense, in parallel, no discussion, no order yet. You want quantity and honesty. The wall looks like a mess on purpose, duplicates and gaps and all.

Board state · just orange events, unordered

Order Placed
Payment Authorized
Meal Delivered
Order Accepted
Meal Cooked
Courier Assigned
Meal Picked Up
Payment Captured
Order Rejected

Facilitator's job here

Protect the silence and protect past tense. "Payment Authorized", not "authorize payment" (that's a command, a different colour, later). Don't let anyone order the stickies yet, and don't resolve duplicates. The mess is data; two people writing the same event slightly differently is a language disagreement worth seeing.

2

The whole room · 25 min

Enforce the timeline: order them left to right

Now the room arranges the same events along a single arrow of time. Duplicates merge, gaps get filled out loud ("wait, what happens between accepted and cooked?"). This is where the narrative of the business appears for the first time, and where people quietly discover they disagreed about the order of things.

Board state · events on one timeline

Order Placed
Payment Authorized
Order Accepted
Meal Cooked
Meal Picked Up
Meal Delivered
timelater
3

The whole room · 15 min

Mark the hotspots and the pivotal events

Two markups on the same timeline. Hotspots (rotated pink) capture every problem, risk and "it depends" so they stop derailing the flow. Pivotal events (the vertical dividers) mark the moments the process changes phase, these almost always become bounded-context seams.

Board state · + hotspots new + pivotal markers

Order Placed
Retry double-charges the card?
Payment Authorized
Order Accepted
Reject after we charged?
Meal Cooked
Meal Picked Up
No courier free at peak?
Meal Delivered
checkoutsettled

The two thick orange dividers are pivotal events: Payment Authorized (commerce becomes a committed order) and Meal Cooked (the kitchen hands off to the road). Watch them, they reappear as context boundaries in step 9.

Why hotspots matter more than they look

Each pink diamond is a future incident, ADR, or 2am page, named early and cheaply. "Retry double-charges the card?" is the idempotency problem from S6. "Reject after we charged?" forces an authorize-then-capture decision and a refund saga. You don't solve them in the storm, you make them visible so they get owned instead of discovered.

4

The whole room · 20 min

Add the actors and the external systems

For each event, ask who made it happen (small yellow actor) and what outside system was involved (pink). This is the first time the wall shows that "FoodHive" is really three different humans and a couple of vendors, not one app.

Board state · + actors & external systems new

Customer
Order Placed
Payment Gateway
Payment Authorized
Restaurant
Order Accepted
Restaurant
Meal Cooked
Courier
Maps / Routing
Meal Picked Up
Courier
SMS / Push
Meal Delivered
checkoutsettled
5

The whole room · 20 min

Add the commands that cause each event

In front of every orange event, place the blue command that triggered it, the actor's intention. Events are facts; commands are decisions. The pairing reads like a sentence: Customer issues Place Order, the result is Order Placed.

Board state · + commands new

Customer
Place Order
Order Placed
Payment Gateway
Authorize Payment
Payment Authorized
Restaurant
Accept Order
Order Accepted
Restaurant
Mark Meal Ready
Meal Cooked
Courier
Pick Up Meal
Meal Picked Up
Courier
Confirm Delivery
Meal Delivered
checkoutsettled

Notice the one command with no human actor in front of it: Authorize Payment is triggered by the system, not a person. That's a clue a policy is firing it automatically, which is exactly step 7.

6

Engineers lead · 20 min

Add the aggregates: what each command lands on

Between command and event sits the aggregate (big yellow), the thing that takes the command, checks its rules, and emits the event. Name them as the room's nouns. When the same aggregate handles several commands in a row, you've found a cluster, an early sketch of a module.

Board state · + aggregates new

Customer
Place Order
Order
Order Placed
Payment Gateway
Authorize Payment
Payment
Payment Authorized
Restaurant
Accept Order
Kitchen Ticket
Order Accepted
Restaurant
Mark Meal Ready
Kitchen Ticket
Meal Cooked
Courier
Pick Up Meal
Delivery
Meal Picked Up
Courier
Confirm Delivery
Delivery
Meal Delivered
checkoutsettled

The aggregates are clustering

Four aggregates appear, and two of them (Kitchen Ticket, Delivery) each own two commands back to back. That repetition is the wall telling you those belong together. Each aggregate is also a unit of consistency (one aggregate, one transaction, from S8), so "Reject after we charged?" is clearly a cross-aggregate problem (Kitchen Ticket vs Payment), which means a saga, not one transaction. The hotspot and the aggregate map already agree.

7

The whole room · 20 min

Add the policies: the "whenever… then…" glue

A policy (lilac) is a reactive rule with no human in the loop: whenever this event happens, then automatically issue that command. Policies are how one event in one part of the business kicks off work in another, and they're exactly the domain events you'll publish between services.

Board state · + policies new (shown under the event they react to)

Place Order
Order
Order Placed
whenever Order Placed → Authorize Payment
Authorize Payment
Payment
Payment Authorized
whenever Payment Authorized → notify Restaurant
Accept Order
Kitchen Ticket
Order Accepted
Mark Meal Ready
Kitchen Ticket
Meal Cooked
whenever Meal Cooked → Assign Courier
Pick Up Meal
Delivery
Meal Picked Up
Confirm Delivery
Delivery
Meal Delivered
whenever Meal Delivered → Capture Payment
checkoutsettled
8

The whole room · 12 min

Add the read models: what each actor looks at to decide

An actor doesn't issue a command in a vacuum, they look at something first. The green read model is that view. Adding them closes the loop (read → decide → command → event) and quietly reveals which data each part of the system actually needs to own or subscribe to.

Board state · + read models new (above the actor who reads them)

Menu & Cart
Customer
Place Order
Order Placed
Payment Gateway
Authorize Payment
Payment Authorized
Incoming Orders
Restaurant
Accept Order
Order Accepted
Kitchen Queue
Restaurant
Mark Meal Ready
Meal Cooked
Available Pickups
Courier
Pick Up Meal
Meal Picked Up
Live Order Tracking
Courier
Confirm Delivery
Meal Delivered
checkoutsettled

Live Order Tracking is read by the customer too, the one read model many parties care about. That's a hint it's a published, cross-context view fed by domain events, not owned by any single aggregate.

9

The whole room · 20 min

The payoff: draw the boundaries the wall already made

Nobody invents the boundaries; you trace the clusters that formed on their own. Group by where the language, the actors, and the aggregates change together, and draw a box. Those boxes are your bounded contexts, and they land exactly on the pivotal events from step 3.

Final board · four bounded contexts emerge new

Place Order
Order
Order Placed
Authorize Payment
Payment
Payment Authorized
Accept Order
Kitchen Ticket
Order Accepted
Mark Meal Ready
Kitchen Ticket
Meal Cooked
Pick Up Meal
Delivery
Meal Picked Up
Confirm Delivery
Delivery
Meal Delivered

Ordering

Order aggregate · the customer's view of the order

Payments

Payment aggregate · authorize, capture, refund

Kitchen

Kitchen Ticket aggregate · accept & cook

Delivery

Delivery aggregate · dispatch & deliver

checkoutsettled

Read it back: this is the whole point

Four bounded contexts (Ordering · Payments · Kitchen · Delivery) discovered, not decreed. The boundaries sit on the pivotal events; the actors don't cross them; each owns its own aggregate and language. The same word "order" even means three things across them (a cart to Ordering, a charge to Payments, a ticket to the Kitchen), which is S8's "same word, different model" signal, made literal. The room agreed in an afternoon, with the restaurant-ops and payments people in the room, instead of three teams discovering the seam by colliding in production.

From the wall to the architecture

Each bounded context becomes a candidate service or module, owning its aggregate and its data, talking to the others only through the domain events on the wall. The board is the first draft of the architecture and the input to ADR #2.

Bounded contextOwns (aggregate)Emits (domain events)Reacts to (policy)
OrderingOrderOrder Placedstarts the flow (customer command)
PaymentsPaymentPayment Authorized, Payment Captured, Payment RefundedOrder Placed → authorize · Meal Delivered → capture · Order Rejected → refund
KitchenKitchen TicketOrder Accepted, Order Rejected, Meal CookedPayment Authorized → present to restaurant
DeliveryDeliveryCourier Assigned, Meal Picked Up, Meal DeliveredMeal Cooked → assign courier

The straight line to S8 and Project 3

This table is ADR #2's evidence. "We split Payments from Ordering because the language, the actor, the data lifecycle, and the failure mode all change at Payment Authorized, and the only coupling is two domain events." That's a service boundary justified by a real seam, not the org chart, which is exactly what P3 · ADR #2 asks for. Whether each context is a service or just a module is still the S7 decision (start as modules, earn the split), but event storming gave you the lines to choose from.

How to actually run one

The logistics that make the difference between a great session and a stalled one.

Set it up

  • Unlimited modelling space. A long wall or a roll of paper, never a whiteboard you'll run out of. Online: a Miro/Mural board with infinite canvas.
  • The right people, not just engineers. Domain experts who know what really happens, plus whoever will build it. 5–8 is the sweet spot.
  • One legend visible (the colour grammar up top) and the scope sentence pinned where everyone sees it.
  • A timebox. Big-picture in 90 min; a full design-level storm across half a day.

The three levels of zoom

  • Big Picture (steps 1–4): events, hotspots, actors. For exploring a whole business and finding the contexts. The most common one.
  • Process Level (steps 5–7): commands, policies, the flow inside one area. For nailing one workflow.
  • Design Level (steps 6–9): aggregates and boundaries, detailed enough to code from.
  • You rarely do all three at once. Start big-picture; zoom into the hotspots that hurt.
!

Traps, and the interview answer

The traps

  • Events in the wrong tense. "Place order" is a command; "Order Placed" is the event. Tense discipline is the whole notation working.
  • Designing instead of discovering. The storm captures how the business works, not your CRUD tables. No databases on the wall.
  • Solving hotspots live. They derail the flow. Park them loudly, return after the timeline is whole.
  • No domain expert in the room. Then it's a guessing session. Engineers alone storm their assumptions, not the business.
  • Boiling the ocean. No scope sentence, and the wall sprawls to the whole company. Bound it first.
  • Skipping the chaos. Going straight to a tidy diagram hides the disagreements that are the entire value.

Interview Q&A

Q · What is event storming, in one breath?

A workshop that maps a domain as a timeline of past-tense events on sticky notes, then adds commands, actors, policies and aggregates, until bounded contexts emerge from the clusters. It's the fastest way to find service boundaries with the business in the room.

Q · Why events first, not data models?

Domain experts can always narrate what happened; nobody argues with "the meal was delivered". Events are the shared language; schemas are a translation that loses meaning. Start where agreement is cheap.

Q · How does it produce boundaries?

Clusters form where the language, the actor, and the aggregate change together, on the pivotal events. You trace the box around each cluster; that's a bounded context, and a candidate service that talks to the rest through domain events.

Companion to S8 · Domain-Driven Design and P3 · ADR #2. Further reading: EventStorming.com (Alberto Brandolini) · Event storming & DDD primer · Fowler · Bounded Context · Introducing EventStorming (book).