install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/events-event-storming" ~/.claude/skills/intense-visions-harness-engineering-events-event-storming && rm -rf "$T"
manifest:
agents/skills/claude-code/events-event-storming/SKILL.mdsource content
Events: Event Storming
Run event storming workshops to discover domain events, commands, and bounded contexts.
When to Use
- You're designing a new system and need to understand the domain before writing code
- You have a complex existing system and need to find service boundaries
- You're migrating a monolith to microservices and need to define bounded contexts
- You want to align developers and domain experts on shared language before implementation
- You're building event-sourced systems and need to identify the full event timeline
Instructions
Event storming process — Big Picture format:
Phase 1: Chaotic exploration (30-60 min)
- Give everyone orange sticky notes
- Rule: every note is a domain event, past tense, specific ("Order Placed", "Payment Failed", "Shipment Delivered")
- Place all stickies on a timeline — left = earlier, right = later
- No debate, no order — just generate events
Phase 2: Enforce timeline (20-30 min)
- Sort events chronologically
- Identify duplicates (merge or keep both if subtly different)
- Ask "what happens before/after this?"
Phase 3: Identify pain points (10-15 min)
- Red stickies = problems, bottlenecks, questions
- "What happens if payment times out here?"
- "Who owns this event?"
Phase 4: Commands (blue stickies)
- For each domain event, ask "what triggered this?"
- Commands are imperative ("Place Order", "Process Payment", "Reserve Stock")
- Place commands before their resulting events
Phase 5: Aggregates (yellow stickies)
- Group commands + events by the object they affect
- "Order" aggregate: Place Order → Order Placed, Cancel Order → Order Cancelled
- Aggregates become your core domain objects
Phase 6: Bounded contexts (pink/purple lines)
- Draw lines around groups of events that belong together
- Each bounded context = one service candidate
- Events that cross lines become integration events
Event map template:
[Command] → [Aggregate] → [Domain Event] → [Read Model] ↓ ↓ [Policy/Rule] [External System]
Translating storming output to code:
// From event storming: "Order" aggregate with events // Commands discovered: PlaceOrder, CancelOrder, ShipOrder // Aggregate root class Order { private events: DomainEvent[] = []; static place(data: PlaceOrderInput): Order { const order = new Order(data); order.record(new OrderPlaced({ orderId: order.id, ...data })); return order; } cancel(reason: string): void { if (this.status === 'SHIPPED') throw new Error('Cannot cancel shipped order'); this.status = 'CANCELLED'; this.record(new OrderCancelled({ orderId: this.id, reason })); } ship(trackingNumber: string): void { if (this.status !== 'PAID') throw new Error('Order must be paid before shipping'); this.status = 'SHIPPED'; this.record(new OrderShipped({ orderId: this.id, trackingNumber })); } pullEvents(): DomainEvent[] { const events = [...this.events]; this.events = []; return events; } private record(event: DomainEvent): void { this.events.push(event); } } // Domain events discovered in storming class OrderPlaced implements DomainEvent { readonly type = 'order.placed'; constructor(public readonly payload: { orderId: string; userId: string; items: Item[] }) {} } class OrderCancelled implements DomainEvent { readonly type = 'order.cancelled'; constructor(public readonly payload: { orderId: string; reason: string }) {} } class OrderShipped implements DomainEvent { readonly type = 'order.shipped'; constructor(public readonly payload: { orderId: string; trackingNumber: string }) {} }
Bounded context communication:
// Events that cross bounded context boundaries become integration events // Integration event from Order context → Inventory context interface OrderPlacedIntegrationEvent { eventId: string; type: 'order.placed'; occurredAt: string; // Only include data the consuming context needs orderId: string; items: { productId: string; quantity: number }[]; } // Inventory context maps this to its own domain language class InventoryContext { handleOrderPlaced(event: OrderPlacedIntegrationEvent): void { // Translate to Inventory language: "stock reservation request" const reservationRequest: StockReservationRequest = { referenceId: event.orderId, items: event.items.map((i) => ({ sku: this.mapProductToSKU(i.productId), quantity: i.quantity, })), }; this.reserveStock(reservationRequest); } }
Details
Sticky note color convention:
- Orange — Domain events (past tense)
- Blue — Commands (imperative)
- Yellow — Aggregates / domain objects
- Pink/Purple — Policies ("whenever X, then Y")
- Red — Pain points / questions
- Green — Read models / views
- Lilac — External systems
Bounded context warning signs:
- Event storms that reference the same concept with different names → translation needed
- Very large aggregates (50+ events) → consider splitting
- Events that are irrelevant to half the participants → wrong bounded context
Remote workshop tools: Miro, Mural, FigJam. Use virtual stickies with the same color coding. Allow async contribution before live sessions.
Transition to code: Each bounded context maps to:
- A separate service (or module in a monolith)
- Its own database schema
- Its own ubiquitous language (shared terms within the context only)
- Integration events for cross-context communication
Anti-patterns:
- Letting developers skip the storming and jump to class diagrams — lose domain knowledge
- Running storming without domain experts — events will be technically-biased, not business-accurate
- Merging all bounded contexts into one to avoid complexity — defeats the purpose
Source
eventstorming.com/
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.