Use this when you have Event Storming outputs (domain events, bounded contexts, thin slices) and need to translate them into a notional system architecture showing how services interact. Boris focuses on relationships between components, not implementation details.
Full guide: See the Architecture Discovery Workshop Playbook for the complete method.
Framework attribution: BORIS modeling builds on Alberto Brandolini's EventStorming method for collaborative domain discovery.
Process
Step 1: Gather inputs
Ask the user:
- Event Storming outputs — domain events, bounded contexts, thin slices, and hot spots. If running as part of
/architecture-discovery, these carry forward automatically. - Target architecture style — microservices, modular monolith, event-driven, or undecided?
- Happy path to model first — which thin slice should we walk through?
- Session format — collaborative workshop or async documentation?
- Output destination — conversation only, Miro, FigJam, Notion, or file?
Step 2: Set up the Boris board
For each bounded context from Event Storming, create a service node:
- Service name — use the bounded context name
- Key responsibilities — what this service owns (2-3 bullet points)
- Domain events it produces — events this service publishes
- Domain events it consumes — events this service subscribes to
Arrange services spatially — related services near each other, external systems at the edges.
Step 3: Walk the happy path
Take the first thin slice and trace it through the Boris diagram:
- Start with the triggering actor/command
- For each step, identify:
- Which service handles it?
- Is the interaction synchronous (API call, request/response) or asynchronous (event, publish/subscribe)?
- What data flows between services?
- Draw arrows between services with labels:
- Solid arrows → synchronous (API calls)
- Dashed arrows → asynchronous (events)
- Note where the flow crosses bounded context boundaries
Step 4: Walk additional flows
Repeat Step 3 for each thin slice:
- Alternate paths
- Exception paths
- Error/compensation flows
Use different colors or annotations to distinguish flows on the same diagram.
Step 5: Identify emerging patterns
As flows accumulate, call out:
- Orchestration vs. choreography — is there a central coordinator or do services react to events independently?
- Shared data concerns — where do two services need the same data?
- Chatty interactions — where are there too many synchronous calls?
- Single points of failure — which service appears in every flow?
- API candidates — which service-to-service interactions imply formal APIs?
Step 6: Generate the Boris model output
Use the Boris template to produce the deliverable:
Boris Model: (Domain/System Name)
Date: (date) Source: Event Storming session (date) Architecture style: (microservices / modular monolith / event-driven / hybrid)
Services
| Service | Responsibilities | Produces Events | Consumes Events |
|---|---|---|---|
| (Service name) | (Key responsibilities) | (Events published) | (Events consumed) |
Interactions
| From | To | Type | Description | Flow |
|---|---|---|---|---|
| (Service A) | (Service B) | Sync/Async | (What's communicated) | (Which thin slice) |
Flow: (Happy Path Name)
Actor → [Service A] --sync--> [Service B] --async--> [Service C]
(command) (API) (event) (sub) (result)
Flow: (Alternate Path Name)
(Same format for each additional flow)
Patterns Identified
| Pattern | Where | Implication |
|---|---|---|
| (e.g., Choreography) | (Which services) | (What this means for implementation) |
API Candidates
| API | Provider | Consumer(s) | Purpose |
|---|---|---|---|
| (API name) | (Service) | (Services) | (What it does) |
Open Questions
- (Question about service boundaries)
- (Question about data ownership)
- (Question about communication patterns)
Step 7: Share and distribute (optional)
If the user requested a tool destination:
- Miro — use
doc_createfor the model document, ordiagram_createfor a visual service diagram - FigJam — use
generate_diagramto create a flowchart showing service interactions - Notion — use
notion-create-pageswith the output as content - File — save to
./output/boris/(domain-name)-boris.md
Step 8: Review
Ask the user:
- Do the service boundaries feel right?
- Are there services that should be split or merged?
- Which synchronous calls should actually be asynchronous?
- Are we ready to move to SNAP documentation, or do we need another modeling pass?
Uncertainty Policy
| Topic | Tolerance | Action |
|---|---|---|
| Service boundaries and ownership | Low | STOP and ask — wrong boundaries create wrong architecture |
| Data flow direction between services | Low | STOP and ask — sync vs. async affects system design fundamentally |
| Interaction type (command/query/event) | Medium | Assume + flag [ASSUMED] — team validates during flow walkthroughs |
| Service naming | Medium | Assume + flag [ASSUMED] — names evolve, consistency matters more |
| Shared data ownership resolution | Medium | Assume + flag [ASSUMED] — flag as design decision for team |
| Non-functional requirements per service | High | Best guess — SNAP documentation captures details |
Default: STOP and ask when a topic is not listed above.
Related skills
/event-storm— previous stage: discover domain events and bounded contexts/snap-document— next stage: document APIs, data, and risks per bounded context/architecture-discovery— skill set that chains all three stages
Output locations
| Format | Location | Notes |
|---|---|---|
| Markdown | Conversation or saved file | Always produced. Source of truth. |
| Miro doc/diagram | User's specified Miro board | Via Miro MCP. |
| FigJam diagram | FigJam | Service interaction flowchart. |
| Notion page | User's specified Notion location | Via Notion MCP. |
| File | ./output/boris/(domain)-boris.md | Local file. |
Example Output
Input
- Event Storming outputs: Domain events:
OrderPlaced,PaymentAuthorized,PaymentDeclined,InventoryReserved,InventoryShortfall,OrderFulfilled,ShipmentCreated,CustomerNotified. Bounded contexts: Order Management, Payment Processing, Inventory, Fulfillment, Notifications. Thin slices: (1) Happy path — customer places order through delivery confirmation, (2) Payment declined — order cancelled and customer notified. - Company / domain: Meridian Supply Co. — B2B wholesale e-commerce platform
- Target architecture style: Event-driven microservices
- Happy path to model first: Thin Slice 1 — successful order through shipment
- Output destination: Conversation + save to file
Output (abbreviated)
Boris Model: Meridian Supply Co. — Order Fulfillment Domain
Date: 2025-07-14 Source: Event Storming session — 2025-07-11 Architecture style: Event-driven microservices
Services
| Service | Responsibilities | Produces Events | Consumes Events |
|---|---|---|---|
| Order Management | Accepts and validates orders; owns order lifecycle state | OrderPlaced, OrderCancelled | PaymentAuthorized, PaymentDeclined, InventoryShortfall, OrderFulfilled |
| Payment Processing | Charges accounts; manages payment retries and declines | PaymentAuthorized, PaymentDeclined | OrderPlaced |
| Inventory | Tracks stock levels; reserves and releases inventory | InventoryReserved, InventoryShortfall | OrderPlaced, OrderCancelled |
| Fulfillment | Picks, packs, and ships orders; owns shipment records | ShipmentCreated, OrderFulfilled | InventoryReserved, PaymentAuthorized |
| Notifications | Sends transactional emails/SMS to buyers and reps | CustomerNotified | OrderPlaced, PaymentDeclined, ShipmentCreated, OrderCancelled |
Interactions
| From | To | Type | Description | Flow |
|---|---|---|---|---|
| Buyer Portal | Order Management | Sync | Submit order command, receive order ID | Slice 1 & 2 |
| Order Management | Payment Processing | Async | Publishes OrderPlaced; Payment subscribes | Slice 1 & 2 |
| Order Management | Inventory | Async | Publishes OrderPlaced; Inventory subscribes | Slice 1 & 2 |
| Payment Processing | Order Management | Async | Publishes PaymentAuthorized or PaymentDeclined | Slice 1 & 2 |
| Inventory | Order Management | Async | Publishes InventoryReserved or InventoryShortfall | Slice 1 & 2 |
| Fulfillment | Order Management | Async | Publishes OrderFulfilled after shipment | Slice 1 |
| Fulfillment | Notifications | Async | ShipmentCreated triggers customer notification | Slice 1 |
| Order Management | Notifications | Async | OrderCancelled triggers decline notification | Slice 2 |
| Buyer Portal | Order Management | Sync | Query order status (polling) | Slice 1 & 2 |
Flow: Happy Path — Successful Order Through Shipment
Buyer --sync--> [Order Management] --async(OrderPlaced)--> [Payment Processing]
--async(OrderPlaced)--> [Inventory]
[Payment Processing] --async(PaymentAuthorized)--> [Order Management]
[Inventory] --async(InventoryReserved) --> [Order Management]
[Order Management] (both confirmations received — internal state: READY)
[Fulfillment] <--async(InventoryReserved + PaymentAuthorized)--
[Fulfillment] --async(ShipmentCreated)--> [Notifications] --> CustomerNotified
[Fulfillment] --async(OrderFulfilled) --> [Order Management] → state: COMPLETE
Flow: Payment Declined — Order Cancelled
Buyer --sync--> [Order Management] --async(OrderPlaced)--> [Payment Processing]
[Payment Processing] --async(PaymentDeclined)--> [Order Management]
[Order Management] → state: CANCELLED
[Order Management] --async(OrderCancelled)--> [Inventory] (release reservation)
[Order Management] --async(OrderCancelled)--> [Notifications] --> CustomerNotified
Patterns Identified
| Pattern | Where | Implication |
|---|---|---|
| Choreography | Payment → Order Mgmt → Fulfillment chain | No central orchestrator; resilient but harder to trace. Consider adding correlation IDs and a saga log. |
| Parallel fan-out | Order Management triggers Payment and Inventory simultaneously | Both must succeed before Fulfillment proceeds — need a join/aggregation mechanism in Order Management. |
| Single point of failure | Order Management appears in every flow | Prioritize high availability and idempotent event handling for this service first. |
| Chatty sync boundary | Buyer Portal polling Order Management for status | Evaluate WebSocket or server-sent events to reduce polling load at scale. |
API Candidates
| API | Provider | Consumer(s) | Purpose |
|---|---|---|---|
| Order Submission API | Order Management | Buyer Portal | Place and validate new orders; return order ID |
| Order Status API | Order Management | Buyer Portal, Internal Ops UI | Query current order state and history |
| Payment Authorization API | Payment Processing | (internal — event-driven, no direct call) | [ASSUMED] May be needed for synchronous retry UI |
| Inventory Availability API | Inventory | Buyer Portal | Check stock levels before order submission |
Open Questions
- Join logic ownership: Should Order Management maintain a saga/state machine waiting for both
PaymentAuthorizedandInventoryReserved, or should Fulfillment own the join? This affects where compensation logic lives. - Partial inventory: What happens when Inventory can fulfill part of an order?
InventoryShortfallis modeled as a full block — partial fulfillment rules are undefined. - Payment retry: Is a declined payment terminal, or can the buyer retry? If retryable, Order Management's state model needs a
PENDING_PAYMENTstate with a timeout. - Notification ownership: Notifications currently subscribes to raw domain events. Should Order Management publish a richer
OrderStatusChangedevent instead, to decouple Notifications from other services' event schemas?
Saved to ./output/boris/meridian-supply-order-fulfillment-boris.md