EventStorming is a collaborative workshop for exploring a business domain through events arranged in time order. Domain experts and developers use the timeline to uncover rules, disagreements, and possible model boundaries. For a .NET team, the result informs use cases and domain objects, but it still requires design work before implementation.
Why Start With Events?
A domain event is a fact: something happened, in past tense, that the business cares about.
OrderPlaced. PaymentCaptured. ShipmentDelayed.
Events make a better modeling primitive than entities for three reasons:
- Everyone owns the vocabulary. A warehouse operator won't debate your class hierarchy, but they will absolutely correct you when your sticky says
OrderShippedand the real sequence isPickListGenerated,OrderPacked,CarrierPickedUp. That correction is domain knowledge you just captured for the price of a sticky note. - Time exposes gaps. Laying events left to right forces the question "what happens between these two?", which is where the undocumented compensations, manual workarounds, and edge cases live.
- Handoffs expose dependencies. Changes in language or ownership suggest possible bounded contexts. Some events remain internal; others later inform explicit integration contracts.
The Grammar: What Each Color Means
EventStorming, created by Alberto Brandolini, is a flexible workshop format with several styles. The following legend is a practical starting point; agree on the colors with participants before the session:
- Orange: domain event. Past tense, business-relevant. The backbone of the wall.
- Blue: command. The intent that caused an event.
PlaceOrderprecedesOrderPlaced. - Small yellow: actor. The person or role issuing a command.
- Pink: external system. The payment provider, the ERP, the carrier API.
- Purple/lilac: policy. The reactive rule connecting an event to a new command: "whenever X happened, do Y".
- Green: read model. The information an actor needs to decide on a command.
- Large yellow: aggregate. The thing that accepts commands and emits events. Deliberately added last.
- Red: hotspot. A question, disagreement, or known pain point. Don't resolve it; flag it and move on.
You don't need all of these on day one. Big picture sessions often use only orange, pink, and red.
Running the Session
Logistics. A wall with 8+ meters of paper (or an infinite Miro/Mural board for remote), fat markers, and no tables between people and the wall. Invite six to twelve people: the developers who have questions and the domain experts who have answers. That second group is non-negotiable; without them you're documenting assumptions, not the domain.
Phase 1: chaotic exploration (60-90 min). One instruction: "write down events that happen in this business, past tense, one per sticky, and put them on the wall roughly in time order." Everyone writes in parallel. No discussion of correctness yet; duplicates are fine, they show what's salient.
Phase 2: enforce the timeline (60 min).
Walk the wall left to right as a group.
Merge duplicates, fix tense ("Ship order" becomes OrderShipped), and physically reorder.
Every argument about ordering is signal: either the order genuinely varies (mark it) or two people run different processes for the same job (definitely mark it).
Put a red sticky on every disagreement and unanswered question.
Phase 3: find the pivotal events (30 min).
Look for events where the language changes and a new cast of actors takes over.
OrderPlaced is where "shopping" vocabulary ends and "fulfillment" vocabulary begins.
PaymentCaptured hands off to accounting's world.
Mark these with vertical tape lines.
Pivotal events suggest boundaries; they do not determine them.
Check each candidate against language, business rules, data ownership, and team responsibilities.
Several pivotal events can occur within one bounded context.
Phase 4 (process/design level): add the machinery. For the area you care most about, layer in blue commands, purple policies, green read models, and finally cluster events around large yellow aggregate stickies. The recurring sentence pattern is: actor issues command on aggregate, which emits event; policy reacts to event and issues the next command.
For the software behavior you choose to model, investigate clusters that would require one aggregate to own too much state. External events and human activities do not necessarily belong to an aggregate in your application.
The facilitator should invite quieter participants to contribute and avoid dictating the model. Time-box unresolved disagreements and assign follow-up owners; do not leave every hotspot permanently parked.
From the Wall to C#
Treat workshop output as input to design. Choose one use case, identify its invariants, and implement enough behavior to test those assumptions with a domain expert.
For an order-placement example, define a domain event and command in Ordering.cs.
This sample uses USD prices loaded from an application-controlled catalog:
public sealed record OrderPlaced(
Guid OrderId,
Guid CustomerId,
decimal Total,
DateTimeOffset OccurredAt);
public sealed record OrderLine(Guid ProductId, int Quantity, decimal UnitPrice);
The command carries the data needed for this use case:
public sealed record PlaceOrderCommand(
Guid CustomerId, IReadOnlyList<OrderLine> Lines);
The candidate aggregate owns the rule that an order must contain valid lines:
public sealed class Order
{
private Order(Guid customerId, OrderLine[] lines, DateTimeOffset now)
{
Id = Guid.NewGuid();
CustomerId = customerId;
Lines = Array.AsReadOnly(lines);
Total = lines.Sum(line => line.Quantity * line.UnitPrice);
PlacedEvent = new OrderPlaced(Id, customerId, Total, now);
}
public Guid Id { get; }
public Guid CustomerId { get; }
public IReadOnlyList<OrderLine> Lines { get; }
public decimal Total { get; }
public OrderPlaced PlacedEvent { get; }
public static Order Place(PlaceOrderCommand command, DateTimeOffset now)
{
if (command.CustomerId == Guid.Empty)
throw new ArgumentException("A customer is required.");
var lines = command.Lines.ToArray();
if (lines.Length == 0 || lines.Any(line =>
line.ProductId == Guid.Empty ||
line.Quantity <= 0 || line.UnitPrice < 0))
{
throw new ArgumentException("The order must contain valid lines.");
}
return new Order(command.CustomerId, lines, now);
}
}
Call Order.Place(command, DateTimeOffset.UtcNow) from an application handler after loading catalog prices.
The returned event records a domain decision; this class has not persisted or published it.
Save the order and any outgoing integration message atomically through an outbox.
A policy such as "reserve stock after an order is placed" suggests another use case. It might become an event handler, a process manager, or a manual task.
A validated bounded context can become a module in a modular monolith. Events crossing that boundary need a public contract, which is the distinction between domain events and integration events. Do not publish every workshop event onto a message bus.
One warning: the wall is a snapshot of understanding, not a specification. Codify the language it produced (event names, aggregate names) into the codebase as the ubiquitous language, and let the details evolve.
What Goes Wrong
The failure modes are predictable, so plan against them:
- No domain experts in the room. You get a beautifully organized wall of developer assumptions. Reschedule rather than run without them.
- The senior architect narrates. One voice writes all the stickies and the session becomes a lecture. The facilitator's job is to hand markers to the quiet people, especially the ones who do the work daily.
- Resolving hotspots live. A 40-minute argument about refund edge cases burns the room's energy. Red sticky, park it, schedule the follow-up.
- Modeling the ideal process. The wall should show the business as it is, workarounds and all. The gap between as-is and to-be is your actual project.
- Stopping without follow-up. Photograph the wall, assign owners to unresolved questions, and record the next decisions. Some workshops improve an operational process without producing software, which is a valid result.
When to Reach for It
EventStorming earns its day when understanding is the bottleneck:
- Kicking off a greenfield system in an unfamiliar domain.
- Before carving a monolith into modules or services, so the boundaries come from the domain instead of the org chart or the database schema.
- Onboarding a team onto a legacy system where the process knowledge lives in three people's heads.
- When two departments use the same words for different things and nobody noticed until integration failed.
It's overkill for a well-understood CRUD feature, and it's not a sprint ritual. It's a knowledge-extraction tool you deploy when the map and the territory have drifted apart.
Summary
EventStorming works because it models behavior before structure, in a language everyone in the room already speaks:
- Orange events in time order expose the real process, including the parts nobody wrote down.
- Pivotal events suggest boundaries to validate against language, ownership, and invariants.
- Commands, policies, and aggregates layer on until the wall reads as executable sentences.
- Choose implementations deliberately: a sticky can describe application behavior, an external event, or a human task.
An initial workshop gives the team a model to challenge. Refine it as domain experts and working code reveal more about the process.
Thanks for reading.
And stay awesome!
Frequently Asked Questions
What is EventStorming?
EventStorming is a workshop format invented by Alberto Brandolini where domain experts and developers model a business process together using sticky notes on a long wall, starting with domain events in past tense and layering in commands, actors, policies, and aggregates.
Who should attend an EventStorming session?
The people with questions (developers, architects) and the people with answers (domain experts, operations staff, product owners). Six to twelve people works well. Without real domain experts in the room, the workshop produces guesses instead of knowledge.
How long does an EventStorming workshop take?
A big picture session covering an entire business line typically takes one full day. Process-level and design-level sessions on a narrower area run two to four hours each. Splitting across multiple shorter sessions also works.
What is the difference between big picture and design-level EventStorming?
Big picture maps an entire domain end to end to find hotspots and context boundaries. Process level zooms into one process, adding commands and policies. Design level goes down to aggregates and invariants, close enough to start writing code.
How do EventStorming results translate to code?
Workshop events suggest candidate domain or integration events, commands suggest use cases, and policies suggest reactions. Validate aggregate and bounded-context boundaries against business invariants, language, and ownership before implementing them; not every sticky becomes code.



