An open API service indexing awesome lists of open source software.

https://github.com/rexebin/event-storming-plugin


https://github.com/rexebin/event-storming-plugin

Last synced: 5 days ago
JSON representation

Awesome Lists containing this project

README

          

# Event Storming Diagram Renderer for GitHub and VS Code

A **browser extension** (Manifest V3) and **VS Code Markdown preview extension** that render **event storming** diagrams from fenced code blocks.

## Features

- 🎨 Full event storming visual language: events, commands, queries, aggregates, actors, policies, views, read models, external systems, errors, and note nodes
- 📦 Container-based layout: JSON DSL containers render as visual boxes for aggregates, external systems, read models, and process containers
- 🗂️ Nested process groups: each child group renders inside a dashed sub-container with the group name in the top-left corner
- ⬅️→ Left-to-right process flows: actor → command/query/policy → event inside containers, with policy failure branches rendered below
- ↘️ Directional arrows with shared-target fan-in layouts for commands and views
- 🔍 Interactive zoom & pan
- 💡 Notes-only tooltips: tooltips appear only for containers, groups, or nodes that have notes
- 📝 Note badges on containers, groups, and nodes that have notes
- 🎯 Collapsible diagrams with toggle button
- 🌈 Color-coded rectangles (black border) per event storming standard:
- **Orange** (`#FFA500`) → Domain Events
- **Light Green** (`#91D49C`) → Commands
- **Dark Green** (`#5BAA62`) → Queries / Read Models
- **Yellow** (`#FEE254`) → Aggregates
- **Yellow** (`#FEE254`) → Views
- **Gray** (`#D4D3D3`) → Actors
- **Blue** (`#859EBF`) → Policies
- **Pink** (`#FB8597`) → External Systems
- **Cyan** (`#8DCFF9`) → Errors
- **Light Yellow** (`#FFF1AA`) → Temp Objects / Notes

## Getting Started

### 1. Install Dependencies

```bash
npm install
```

### 2. Build

```bash
npm run build
```

This bundles TypeScript (via esbuild IIFE) and copies assets into `dist/`.

### 3. Run Tests

```bash
npm test
```

### 4. Load in Edge (or Chrome)

1. Open `edge://extensions/` (or `chrome://extensions/`)
2. Enable **Developer mode** (toggle in top-right)
3. Click **Load unpacked**
4. Select the `dist/` folder

### 5. Package for VS Code

```bash
npm run package:vscode
```

This creates a `.vsix` file that can be shared or installed using **Extensions: Install from VSIX...**

## Usage on GitHub

In any Markdown file (README, issue, PR comment, wiki), you can use:

- `eventstorming` for the text DSL
- `json` for the JSON DSL, which gives editors JSON syntax help

```json
[
{
"type": "Aggregate",
"name": "User",
"notes": ["This aggregate represents a user in the system, including their registration and profile management processes."],
"children": [
{
"name": "User Registration",
"nodes": [
{ "type": "Actor", "name": "Customer1", "next": "Register" },
{ "type": "Command", "name": "Register", "next": "Is Email Valid?" },
{ "type": "Policy", "name": "Is Email Valid?", "next": "UserRegistered", "negativeNext": "Invalid Email" },
{ "type": "Error", "name": "Invalid Email", "notes": ["The email address provided is not valid. Please enter a valid email address and try again."] },
{ "type": "Event", "name": "UserRegistered", "next":"Some Note" },
{ "type": "Note", "name": "Some Note", "notes": ["This is a note attached to the UserRegistered event."] }
],
"notes": ["This process handles user registration, including validating the email address and creating a new user account if the email is valid."]
}
]
},
{
"type": "Aggregate",
"name": "Morning Routine",
"children": [
{
"name": "Wake Up",
"nodes": [
{ "type": "Actor", "name": "Me", "next": "Wake Up" },
{ "type": "Command", "name": "Wake Up", "next": "Is Alarm Ringing?" },
{ "type": "Policy", "name": "Is Alarm Ringing?", "next": "Got Out of Bed", "negativeNext": "Sleep In" },
{ "type": "Error", "name": "Sleep In" },
{ "type": "Event", "name": "Got Out of Bed" }
]
},
{
"name": "Shower",
"nodes": [
{ "type": "Command", "name": "Have Shower", "next": "Is the shower running?" },
{ "type": "Policy", "name": "Is the shower running?", "next": "Have shower gel?", "negativeNext": "Switch on shower" },
{ "type": "ExternalSystem", "name": "Switch on shower", "next": "Have shower gel?" },
{ "type": "Policy", "name": "Have shower gel?", "next": "Had Shower", "negativeNext": "Go Buy Shower Gel" },
{ "type": "Error", "name": "Go Buy Shower Gel" },
{ "type": "Event", "name": "Had Shower" }
]
}
]
},
{
"type": "Aggregate",
"name": "User Profile",
"children": [
{
"name": "Update Profile",
"nodes": [
{ "type": "Actor", "name": "Customer", "next": "UpdateProfile" },
{ "type": "Command", "name": "UpdateProfile", "next": "Is User Authenticated?" },
{ "type": "Policy", "name": "Is User Authenticated?", "next": "ProfileUpdated", "negativeNext": "Authentication Required" },
{ "type": "Error", "name": "Authentication Required", "notes": ["You must be logged in to update your profile. Please log in and try again."] },
{ "type": "Event", "name": "ProfileUpdated" }
],
"notes": ["This process allows users to update their profile information, but only if they are authenticated. If the user is not authenticated, an error is returned."]
}
]
},
{
"type": "Aggregate",
"name": "Order",
"children": [
{
"name": "Place Order",
"nodes": [
{ "type": "Command", "name": "PlaceOrder", "next": "InventoryService" },
{ "type": "ExternalSystem", "name": "InventoryService", "next": "Do We Have Stock?" },
{ "type": "Policy", "name": "Do We Have Stock?", "next": "Is Order Detail Valid?", "negativeNext": "Out Of Stock" },
{ "type": "Policy", "name": "Is Order Detail Valid?", "next": "PaymentGateway", "negativeNext": "Invalid Order Detail" },
{ "type": "Error", "name": "Invalid Order Detail", "notes": ["Order details are invalid, please review your order and try again."] },
{ "type": "ExternalSystem", "name": "PaymentGateway", "next": "Is Payment Successful?" },
{ "type": "Policy", "name": "Is Payment Successful?", "next": "OrderPlaced", "negativeNext": "PaymentFailed" },
{ "type": "Error", "name": "PaymentFailed", "notes": ["Payment failed, please try again or use a different payment method.", "Client should handle this error."] },
{ "type": "Event", "name": "OrderPlaced" }
]
},
{
"name": "Cancel Order",
"nodes": [
{ "type": "Actor", "name": "Customer", "next": "CancelOrder" },
{ "type": "Actor", "name": "Staff", "next": "CancelOrder" },
{ "type": "Event", "name": "PaymentFailed", "next": "CancelOrder" },
{ "type": "Command", "name": "CancelOrder", "next": "Is Cancellation Allowed?" },
{ "type": "Policy", "name": "Is Cancellation Allowed?", "next": "OrderCancelled", "negativeNext": "CancellationDenied" },
{ "type": "Event", "name": "OrderCancelled", "notes": ["Order has been cancelled successfully.", "another note"] }
]
}
]
},
{
"type": "ExternalSystem",
"name": "Inventory Service",
"children": [
{ "name": "Inventory Check", "nodes": [
{"type": "Command", "name": "Check Inventory", "next": "Get Inventory"},
{"type": "Query", "name": "Get Inventory", "next": "Has Stock?"},
{"type": "Policy", "name": "Has Stock?", "next": "InventoryCheckPassed", "negativeNext": "Out of Stock"},
{"type": "Event", "name": "Inventory Check Passed"}
]}
]
},
{
"type": "ReadModel",
"name": "OrderDetail",
"children": [
{
"name": "Order Detail Projection",
"nodes": [
{ "type": "Event", "name": "OrderPlaced", "next": "Order Detail View" },
{ "type": "Event", "name": "OrderCancelled", "next": "Order Detail View" },
{ "type": "Event", "name": "OrderUpdated", "next": "Order Detail View" },
{ "type": "Event", "name": "OrderShipped", "next": "Order Detail View" },
{ "type": "View", "name": "Order Detail View", "notes": ["This view is used to display the details of an order, including its status, items, and other relevant information."] }
]
}
]
},
{
"type": "Process",
"name": "Customer Order View",
"children": [
{
"name": "View Order Details",
"nodes": [
{ "type": "Actor", "name": "Customer", "next": "GetOrderDetails" },
{ "type": "Query", "name": "GetOrderDetails", "next": "Order Detail Projection" },
{ "type": "View", "name": "Order Detail Projection" }
]
}
]
}
]
```

The diagram will render inline, replacing the code block automatically.

For the JSON DSL shown above, supported container types are `Aggregate`, `ExternalSystem`, `ReadModel`, and `Process`.

## Usage in VS Code

Open a Markdown file containing an `eventstorming` fenced block or a matching `json` fenced block, then run **Markdown: Open Preview to the Side**.

The built-in Markdown preview keeps normal Markdown rendering and replaces matching blocks with the diagram UI.

> **Note:** The current VS Code integration targets the desktop Markdown preview.

## DSL Reference

The current DSL is JSON. Its shape matches the implementation in `src/dsl-type.ts`:

```ts
type ContainerType = "Aggregate" | "ExternalSystem" | "ReadModel" | "Process";

type NodeType =
| "Aggregate"
| "Actor"
| "Command"
| "Event"
| "Query"
| "Policy"
| "Error"
| "ExternalSystem"
| "Note"
| "View";

interface Container {
type: ContainerType;
name: string;
notes?: string[];
children: Group[];
}

interface Group {
name: string;
nodes: Node[];
notes?: string[];
}

interface Node {
type: NodeType;
name: string;
next?: string;
negativeNext?: string;
notes?: string[];
}
```

### Schema Notes

- `children` defines independent process groups inside a container.
- `next` links to another node in the same group and renders to the **right**.
- `negativeNext` is used for policy failure paths and renders **below** the policy.
- If a policy omits a matching negative-path node, the renderer creates a default error node.
- `notes` can be added to containers, groups, or nodes. Elements with notes show a small `i` badge, and hovering them shows a notes-only tooltip.
- `Note` is a supported JSON node type and renders as a light-yellow note node rather than a command.

## Project Structure

```
├── src/ # TypeScript source
│ ├── content.ts # GitHub content script
│ ├── dsl.ts # DSL parser + types
│ ├── dsl-type.ts # Container/Process type definitions
│ ├── dsl.test.ts # DSL parser tests
│ ├── renderer.ts # D3.js diagram renderer
│ ├── renderer.test.ts # Renderer tests
│ └── types.d.ts # D3 type declarations
├── dist/ # Built output (load this as extension)
│ ├── manifest.json
│ ├── content.js # Single bundled file (IIFE)
│ └── style.css
├── package.json
├── tsconfig.json
├── build.mjs # esbuild bundler
└── vitest.config.ts # Test configuration
```

### Testing

73 tests (51 DSL + 22 renderer) using Vitest with jsdom:

```bash
npm test # Run once
npm run test:watch # Watch mode
```

## License

MIT