@eventmodelers/cli 1.0.37 → 1.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/shared/skills/learn-eventmodelers-api/SKILL.md +10 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +3 -61
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +24 -93
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +2 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-core-rules/SKILL.md +182 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/SKILL.md +23 -7
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +50 -223
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/references/examples.md +30 -120
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +3 -78
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +3 -50
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +3 -54
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-interview-protocol/SKILL.md +31 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +27 -67
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +15 -15
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +44 -52
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +14 -15
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +4 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +3 -15
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +3 -50
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +4 -56
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +58 -97
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +72 -127
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +15 -1
- package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +11 -1
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md
CHANGED
|
@@ -11,14 +11,14 @@ allowed-tools:
|
|
|
11
11
|
|
|
12
12
|
> **Before doing anything else**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until it has completed. Consult `learn-eventmodelers-api` only if you need to look up a specific endpoint or field this file doesn't cover — don't load it eagerly.
|
|
13
13
|
|
|
14
|
+
This step applies the shared element rules in **`eventmodeling-core-rules`** — read it once per session if you haven't already; it defines what a COMMAND/EVENT/READMODEL/SCREEN/AUTOMATION is, how each is named, and the anti-patterns to reject, so this step doesn't restate them.
|
|
15
|
+
|
|
14
16
|
Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
15
17
|
|
|
16
18
|
## Interview Phase (Optional)
|
|
17
19
|
|
|
18
20
|
**When to Interview**: Skip if the user has already identified UI actions/commands and processor triggers. Interview when it's unclear which actions are user-initiated vs. processor-automated.
|
|
19
21
|
|
|
20
|
-
**Interview Strategy**: Separate UI-driven commands from processor-driven commands before cataloging inputs. Mixing them leads to incorrect role attribution, which breaks the Role Catalog traceability that downstream steps depend on.
|
|
21
|
-
|
|
22
22
|
### Critical Questions
|
|
23
23
|
|
|
24
24
|
1. **Automation Level** (Impact: Determines which commands are UI-triggered vs. processor-triggered)
|
|
@@ -31,54 +31,7 @@ Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect`
|
|
|
31
31
|
- Why it matters: External triggers are processor commands, not UI commands
|
|
32
32
|
- Follow-up triggers: If (B) or (D) → ask which external systems send webhooks and what data they include
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
**Conditional Entry**:
|
|
37
|
-
```
|
|
38
|
-
If user has provided:
|
|
39
|
-
- UI actions already listed per storyboard screen
|
|
40
|
-
- AND processor triggers identified with source systems named
|
|
41
|
-
- AND it's clear which role/actor initiates each action
|
|
42
|
-
|
|
43
|
-
Then: Skip interview, proceed directly to command identification
|
|
44
|
-
|
|
45
|
-
Else: Conduct interview
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
**Phase 1: Trigger Classification** (Question 1)
|
|
49
|
-
- Establish which commands come from human actors vs. automated processors
|
|
50
|
-
- Confirm Role Catalog from Step 1 is available for attribution
|
|
51
|
-
|
|
52
|
-
**Phase 2: External Triggers** (Question 2)
|
|
53
|
-
- Identify all external system integrations that issue commands
|
|
54
|
-
- Confirm whether scheduled jobs or event-driven processors exist
|
|
55
|
-
|
|
56
|
-
### Capturing Interview Findings
|
|
57
|
-
|
|
58
|
-
Append findings to the project's event modeling file:
|
|
59
|
-
|
|
60
|
-
**File**: `.trogonai/interviews/[project-name]/EVENTMODELING.md`
|
|
61
|
-
|
|
62
|
-
Use Write tool to add/update this section:
|
|
63
|
-
|
|
64
|
-
```markdown
|
|
65
|
-
## 4. Identifying Inputs (eventmodeling-identifying-inputs)
|
|
66
|
-
|
|
67
|
-
### Automation Classification
|
|
68
|
-
[From Q1: Which actions are user-initiated vs. processor-automated?]
|
|
69
|
-
|
|
70
|
-
### External System Triggers
|
|
71
|
-
[From Q2: Which external systems trigger commands? Webhook formats?]
|
|
72
|
-
|
|
73
|
-
### Command Attribution Summary
|
|
74
|
-
- UI-issued commands: [list with role from Role Catalog]
|
|
75
|
-
- Processor-issued commands: [list with source system]
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Update Interview Trail:
|
|
79
|
-
```markdown
|
|
80
|
-
| 4 | eventmodeling-identifying-inputs | Done | UI commands, processor commands, role attribution |
|
|
81
|
-
```
|
|
34
|
+
Follow **`eventmodeling-interview-protocol`** to run this interview and record its findings — label this step "**4. Identifying Inputs** (`eventmodeling-identifying-inputs`)". Findings should cover: automation classification (user-initiated vs. processor-automated), external system triggers, and a command attribution summary (UI-issued vs. processor-issued, each with its role/source).
|
|
82
35
|
|
|
83
36
|
---
|
|
84
37
|
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md
CHANGED
|
@@ -11,14 +11,14 @@ allowed-tools:
|
|
|
11
11
|
|
|
12
12
|
> **Before doing anything else**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until it has completed. Consult `learn-eventmodelers-api` only if you need to look up a specific endpoint or field this file doesn't cover — don't load it eagerly.
|
|
13
13
|
|
|
14
|
+
This step applies the shared element rules in **`eventmodeling-core-rules`** — read it once per session if you haven't already; it defines what a COMMAND/EVENT/READMODEL/SCREEN/AUTOMATION is, how each is named, and the anti-patterns to reject, so this step doesn't restate them.
|
|
15
|
+
|
|
14
16
|
Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
15
17
|
|
|
16
18
|
## Interview Phase (Optional)
|
|
17
19
|
|
|
18
20
|
**When to Interview**: Skip if the user has clearly identified: read model queries needed by UI, processor needs, and refresh patterns. Interview when unclear which data queries are critical or how frequently they're accessed.
|
|
19
21
|
|
|
20
|
-
**Interview Strategy**: Establish query patterns and identify any calculations before designing read models. The most common architecture error at this step is modeling recalculated state as an event — identifying calculated fields upfront prevents that anti-pattern.
|
|
21
|
-
|
|
22
22
|
### Critical Questions
|
|
23
23
|
|
|
24
24
|
1. **Query Patterns** (Impact: Determines which read models are needed and their update frequency)
|
|
@@ -31,58 +31,7 @@ Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect`
|
|
|
31
31
|
- Why it matters: Common mistake to model calculations as events; identifying them upfront prevents architecture errors
|
|
32
32
|
- Follow-up triggers: For each calculated field mentioned → confirm "This recalculates as source data changes, so it belongs in a read model projection — does that match your expectation?"
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
**Conditional Entry**:
|
|
37
|
-
```
|
|
38
|
-
If user has provided:
|
|
39
|
-
- UI screens with data needs mapped to event sources
|
|
40
|
-
- AND processor query needs documented
|
|
41
|
-
- AND calculated/aggregated fields identified as read models (not events)
|
|
42
|
-
|
|
43
|
-
Then: Skip interview, proceed directly to read model design
|
|
44
|
-
|
|
45
|
-
Else: Conduct interview
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
**Phase 1: Query Pattern Mapping** (Question 1)
|
|
49
|
-
- Identify which UI screens and processors need which data
|
|
50
|
-
- Typically every screen needs some kind of data, same for automations.
|
|
51
|
-
- Establish freshness requirements per consumer
|
|
52
|
-
- Determine if any queries require real-time consistency
|
|
53
|
-
|
|
54
|
-
**Phase 2: Calculation Detection** (Question 2)
|
|
55
|
-
- Surface any aggregated or computed values
|
|
56
|
-
- Confirm they are projections, not events
|
|
57
|
-
- Prevent the calculation-as-event anti-pattern before design begins
|
|
58
|
-
|
|
59
|
-
### Capturing Interview Findings
|
|
60
|
-
|
|
61
|
-
Append findings to the project's event modeling file:
|
|
62
|
-
|
|
63
|
-
**File**: `.trogonai/interviews/[project-name]/EVENTMODELING.md`
|
|
64
|
-
|
|
65
|
-
Use Write tool to add/update this section:
|
|
66
|
-
|
|
67
|
-
```markdown
|
|
68
|
-
## 5. Identifying Outputs (eventmodeling-identifying-outputs)
|
|
69
|
-
|
|
70
|
-
### Query Patterns
|
|
71
|
-
[From Q1: Which consumers need what freshness? Real-time vs. periodic?]
|
|
72
|
-
|
|
73
|
-
### Calculated Fields Identified
|
|
74
|
-
[From Q2: Which fields are aggregated/calculated? Confirmed as read models?]
|
|
75
|
-
|
|
76
|
-
### Read Model Summary
|
|
77
|
-
- Real-time read models: [list]
|
|
78
|
-
- Near-real-time read models: [list]
|
|
79
|
-
- Calculation-as-event anti-patterns caught: [list or "None"]
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Update Interview Trail:
|
|
83
|
-
```markdown
|
|
84
|
-
| 5 | eventmodeling-identifying-outputs | Done | Read model catalog, query patterns, calculation classification |
|
|
85
|
-
```
|
|
34
|
+
Follow **`eventmodeling-interview-protocol`** to run this interview and record its findings — label this step "**5. Identifying Outputs** (`eventmodeling-identifying-outputs`)". Findings should cover: query patterns (which consumers need what freshness), calculated/aggregated fields identified (and confirmed as read models, not events), and a read model summary by freshness tier.
|
|
86
35
|
|
|
87
36
|
---
|
|
88
37
|
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-interview-protocol/SKILL.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eventmodeling-interview-protocol
|
|
3
|
+
description: Shared procedure for deciding whether to ask the user clarifying questions before an event-modeling step proceeds, and how to record what was decided. Not a step of its own — referenced by other eventmodeling-* skills' Interview Phase section. Do not use standalone.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Interview Protocol
|
|
7
|
+
|
|
8
|
+
Shared by every event-modeling step that has an "Interview Phase" section. A step's own SKILL.md defines only what's specific to it — when to interview and its Critical Questions. Everything below is common to all of them.
|
|
9
|
+
|
|
10
|
+
## When to ask
|
|
11
|
+
|
|
12
|
+
Missing information, or something ambiguous that this step needs? Ask the user — using the step's own Critical Questions as a guide, via `AskUserQuestion` where appropriate. Follow any listed follow-up trigger based on the answer.
|
|
13
|
+
|
|
14
|
+
Already have everything the step needs? Skip straight to the step's own workflow — don't ask questions whose answers you already have.
|
|
15
|
+
|
|
16
|
+
**Unless told not to ask** (an autonomous/unattended run, or the user has said not to stop for questions): don't block. Proceed with the most reasonable assumption for anything missing, and say so plainly in this step's findings below — visibly, so it can be corrected later. Never silently guess.
|
|
17
|
+
|
|
18
|
+
## Recording the outcome
|
|
19
|
+
|
|
20
|
+
Append to the project's event modeling file: `.trogonai/interviews/[project-name]/EVENTMODELING.md`, under a section for this step:
|
|
21
|
+
|
|
22
|
+
```markdown
|
|
23
|
+
## <Step Number>. <Step Name> (<skill-name>)
|
|
24
|
+
[What was asked and decided — or, if nothing was asked, what was assumed and why]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then add or update this step's row in the `## Interview Trail` table (create it if this is the first step to run):
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+
| <Step Number> | <skill-name> | Done | <one-line summary of key outputs> |
|
|
31
|
+
```
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: eventmodeling-optimizing-stream-design
|
|
3
|
-
description: "Validate and fix event
|
|
3
|
+
description: "Validate and fix an entity's event timeline boundary — is it anchored on a single business identity, or is it secretly a collection/log that will grow unbounded? Use when concerned about a timeline mixing concerns, reviewing timeline boundaries, or when a timeline feels like it's absorbing unrelated events. Do not use for: designing the initial event model structure (use eventmodeling-designing-event-models) or general model validation (use eventmodeling-validating-event-models)."
|
|
4
4
|
allowed-tools:
|
|
5
5
|
- AskUserQuestion
|
|
6
6
|
- Write
|
|
@@ -8,102 +8,64 @@ allowed-tools:
|
|
|
8
8
|
|
|
9
9
|
# Optimizing Stream Design
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
This step applies the shared element rules in **`eventmodeling-core-rules`** — read it once per session if you haven't already; it defines what a COMMAND/EVENT/READMODEL/SCREEN/AUTOMATION is, how each is named, and the anti-patterns to reject, so this step doesn't restate them.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Timeline Boundary Optimization
|
|
14
14
|
|
|
15
|
-
**
|
|
16
|
-
|
|
17
|
-
### Critical Question
|
|
18
|
-
|
|
19
|
-
**Stream Identity** (Impact: Determines whether the stream is a real aggregate or a disguised collection/log)
|
|
20
|
-
- Question: "What one business entity does this stream represent the history of? Does every event in it belong to that entity's own lifecycle, or does it also pick up events from a broader category?"
|
|
21
|
-
- Why it matters: A stream with no single identity, or one that mixes in events belonging to other entities, is the root cause behind almost every stream-design complaint — not stream length itself.
|
|
22
|
-
- Follow-up triggers: If the answer names a category ("all orders", "system events") rather than one entity → that's Pattern 3/4 in `references/patterns.md` (Collection/Event Log anti-patterns) — walk through the fix directly.
|
|
23
|
-
|
|
24
|
-
### Capturing Interview Findings
|
|
25
|
-
|
|
26
|
-
Append findings to the project's event modeling file:
|
|
27
|
-
|
|
28
|
-
**File**: `.trogonai/interviews/[project-name]/EVENTMODELING.md`
|
|
29
|
-
|
|
30
|
-
Use Write tool to add/update this section:
|
|
31
|
-
|
|
32
|
-
```markdown
|
|
33
|
-
## Optimizing Stream Design (eventmodeling-optimizing-stream-design)
|
|
34
|
-
|
|
35
|
-
### Stream Identity Review
|
|
36
|
-
[Which streams were reviewed? What entity does each represent? Any that turned out to be a disguised collection/log?]
|
|
37
|
-
|
|
38
|
-
### Boundary Decisions
|
|
39
|
-
- Streams requiring redesign: [list or "None"]
|
|
40
|
-
- Streams confirmed well-bounded: [list]
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Update Interview Trail:
|
|
44
|
-
```markdown
|
|
45
|
-
| Optimization | eventmodeling-optimizing-stream-design | Done | Stream identity review, boundary decisions |
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## Stream Design Optimization
|
|
51
|
-
|
|
52
|
-
**Purpose**: Validate stream boundaries — confirm each stream is anchored on a single business entity's own lifecycle, and catch the two common anti-patterns (an unbounded collection, or an unrelated event log) before they get built.
|
|
15
|
+
**Purpose**: Validate timeline boundaries — confirm each entity's event timeline is anchored on a single business entity's own lifecycle, and catch the two common anti-patterns (an unbounded collection, or an unrelated event log) before they get modeled further.
|
|
53
16
|
|
|
54
17
|
**Applies To**: Any domain — e-commerce, banking, SaaS, marketplace, healthcare, etc.
|
|
55
18
|
|
|
56
19
|
**When to Use**:
|
|
57
|
-
- After defining
|
|
58
|
-
-
|
|
59
|
-
- When a
|
|
60
|
-
- When redesigning a stream whose boundary turned out to be wrong
|
|
20
|
+
- After defining events in domain analysis
|
|
21
|
+
- When a timeline feels like it's growing for reasons unrelated to one entity's own story
|
|
22
|
+
- When redesigning a timeline whose boundary turned out to be wrong
|
|
61
23
|
|
|
62
24
|
**What It Does**:
|
|
63
|
-
1. Checks that each
|
|
64
|
-
2. Identifies
|
|
65
|
-
3. Recommends the correct
|
|
66
|
-
4. Distinguishes "this
|
|
25
|
+
1. Checks that each timeline has a single, natural business identity
|
|
26
|
+
2. Identifies timelines that are actually collections or logs in disguise
|
|
27
|
+
3. Recommends the correct boundary when one is wrong
|
|
28
|
+
4. Distinguishes "this timeline is long because the entity has a long history" (fine) from "this timeline is long because it's absorbing events that don't belong to it" (a boundary bug)
|
|
67
29
|
|
|
68
30
|
---
|
|
69
31
|
|
|
70
32
|
## Core Principle: Get the Identity Right
|
|
71
33
|
|
|
72
34
|
**Golden Rule**:
|
|
73
|
-
> If a
|
|
35
|
+
> If a timeline feels like it's growing for the wrong reasons, first ask: "Does this timeline actually have one business identity, or is it a collection/log wearing an entity's name?"
|
|
74
36
|
|
|
75
|
-
A long
|
|
37
|
+
A long timeline is not, by itself, evidence of a design problem — an account open for thirty years is correctly one long timeline. What *is* always a design problem is a timeline whose events don't all belong to the same entity's own lifecycle.
|
|
76
38
|
|
|
77
39
|
---
|
|
78
40
|
|
|
79
|
-
##
|
|
41
|
+
## Timeline Boundary Review
|
|
80
42
|
|
|
81
|
-
For each
|
|
43
|
+
For each entity's timeline in scope, work through:
|
|
82
44
|
|
|
83
45
|
### 1. Name the identity
|
|
84
46
|
|
|
85
|
-
What single business entity does this
|
|
47
|
+
What single business entity does this timeline represent the history of? Write it down explicitly (e.g. `orderId`, not "orders").
|
|
86
48
|
|
|
87
49
|
### 2. Check every event against that identity
|
|
88
50
|
|
|
89
|
-
Does each event
|
|
51
|
+
Does each event on the timeline describe something that happened to *this* entity — not to a category of entities, not to the system in general? If any event fails this test, the boundary is wrong (see `references/patterns.md`'s Red Flags).
|
|
90
52
|
|
|
91
53
|
### 3. Classify the result
|
|
92
54
|
|
|
93
55
|
| Result | Meaning | Action |
|
|
94
56
|
|---|---|---|
|
|
95
57
|
| Every event belongs to one clear entity | Boundary is correct | Keep as-is, regardless of how long the history gets |
|
|
96
|
-
| Events span a category or "all X" | Pattern 3 (Collection) anti-pattern | Re-scope to the real per-entity identity; the category becomes a read model/query, not
|
|
97
|
-
| Events span unrelated concerns (users, orders, payments mixed) | Pattern 4 (Event Log) anti-pattern | Split into one
|
|
98
|
-
|
|
|
58
|
+
| Events span a category or "all X" | Pattern 3 (Collection) anti-pattern | Re-scope to the real per-entity identity; the category becomes a read model/query, not an event timeline |
|
|
59
|
+
| Events span unrelated concerns (users, orders, payments mixed) | Pattern 4 (Event Log) anti-pattern | Split into one timeline per concern |
|
|
60
|
+
| Timeline conflates an entity's active life with its historical record | Missing the Pattern 5 split | Separate active vs. archived/historical timelines |
|
|
99
61
|
|
|
100
62
|
---
|
|
101
63
|
|
|
102
64
|
## Reference Files
|
|
103
65
|
|
|
104
|
-
**
|
|
105
|
-
- 5
|
|
106
|
-
- The
|
|
66
|
+
**Timeline Boundary Design**: See [patterns.md](references/patterns.md) for:
|
|
67
|
+
- 5 boundary patterns (single entity, composite, collection anti-pattern, event-log anti-pattern, historical)
|
|
68
|
+
- The timeline boundary decision tree
|
|
107
69
|
- Red flags that indicate the boundary — not the volume — is wrong
|
|
108
70
|
- Tips for finding the right boundary
|
|
109
71
|
|
|
@@ -116,9 +78,7 @@ Does each event in the stream describe something that happened to *this* entity
|
|
|
116
78
|
|
|
117
79
|
## Quality Checklist
|
|
118
80
|
|
|
119
|
-
- [ ] Each
|
|
120
|
-
- [ ] Every event
|
|
121
|
-
- [ ] No
|
|
122
|
-
- [ ]
|
|
123
|
-
- [ ] Each stream can be independently versioned and replayed without affecting other streams
|
|
124
|
-
- [ ] A stream that conflates an entity's active life with its historical record has been split (active vs. archived)
|
|
81
|
+
- [ ] Each timeline is identified by a business entity identity (e.g., `orderId`), not a category or type
|
|
82
|
+
- [ ] Every event on a timeline belongs to that one entity's own lifecycle — none of them describe a different entity or an unrelated system concern
|
|
83
|
+
- [ ] No timeline is secretly a collection ("all X") or a log ("everything that happened") wearing an entity's name
|
|
84
|
+
- [ ] A timeline that conflates an entity's active life with its historical record has been split (active vs. archived)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Domain-Specific
|
|
1
|
+
# Domain-Specific Timeline Boundary Patterns
|
|
2
2
|
|
|
3
3
|
## Contents
|
|
4
4
|
- E-commerce patterns
|
|
@@ -11,7 +11,7 @@ Each entry shows a well-scoped boundary for that domain: what identity it's anch
|
|
|
11
11
|
|
|
12
12
|
## E-commerce Domain
|
|
13
13
|
|
|
14
|
-
**Order
|
|
14
|
+
**Order Entity**:
|
|
15
15
|
```
|
|
16
16
|
Identity: orderId
|
|
17
17
|
Lifetime: 1-3 years
|
|
@@ -20,17 +20,17 @@ Reason for this boundary: the order has a clear owner (one customer,
|
|
|
20
20
|
one checkout) and a natural end (delivered, cancelled, refunded)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
**Shopping Cart
|
|
23
|
+
**Shopping Cart Entity**:
|
|
24
24
|
```
|
|
25
25
|
Identity: cartId (or customerId, if a customer has exactly one active cart)
|
|
26
26
|
Lifetime: 30 minutes to 2 years (varies widely by product)
|
|
27
27
|
Boundary: Items added/removed for this one cart
|
|
28
28
|
Watch for: an "abandoned" cart and an "active" cart are different
|
|
29
|
-
lifecycles — split into separate
|
|
29
|
+
lifecycles — split into separate timelines if their behavior diverges
|
|
30
30
|
(e.g. abandoned-cart recovery vs. active checkout flow)
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
**User Account
|
|
33
|
+
**User Account Entity**:
|
|
34
34
|
```
|
|
35
35
|
Identity: userId
|
|
36
36
|
Lifetime: 5-10+ years
|
|
@@ -44,26 +44,26 @@ different concerns with different change frequency — split them
|
|
|
44
44
|
|
|
45
45
|
## Banking Domain
|
|
46
46
|
|
|
47
|
-
**Account
|
|
47
|
+
**Account Entity**:
|
|
48
48
|
```
|
|
49
49
|
Identity: accountId
|
|
50
50
|
Lifetime: 10-50+ years
|
|
51
51
|
Boundary: Deposits, withdrawals, and fees for this one account
|
|
52
52
|
Watch for: an account spanning decades is still one identity — the
|
|
53
|
-
boundary is correct even though the
|
|
53
|
+
boundary is correct even though the timeline is long-lived; don't
|
|
54
54
|
split it just because it accumulates many events over a long life
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
**Transaction
|
|
57
|
+
**Transaction Entity**:
|
|
58
58
|
```
|
|
59
59
|
Identity: transactionId
|
|
60
60
|
Lifetime: 1-2 months (then archived)
|
|
61
61
|
Boundary: Requested → Processing → Settled, for this one transaction
|
|
62
62
|
Reason for this boundary: a transaction is a short, self-contained
|
|
63
|
-
lifecycle — treat it as its own
|
|
63
|
+
lifecycle — treat it as its own timeline, not folded into the account
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
**Loan
|
|
66
|
+
**Loan Entity**:
|
|
67
67
|
```
|
|
68
68
|
Identity: loanId
|
|
69
69
|
Lifetime: 5-30 years
|
|
@@ -77,7 +77,7 @@ ActiveLoan vs. CompletedLoan if their consumers genuinely differ
|
|
|
77
77
|
|
|
78
78
|
## SaaS Domain
|
|
79
79
|
|
|
80
|
-
**Subscription
|
|
80
|
+
**Subscription Entity**:
|
|
81
81
|
```
|
|
82
82
|
Identity: subscriptionId
|
|
83
83
|
Lifetime: 1-5+ years
|
|
@@ -86,17 +86,17 @@ Reason for this boundary: a subscription has one clear owner and a
|
|
|
86
86
|
well-defined lifecycle
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
**User Workspace
|
|
89
|
+
**User Workspace Entity**:
|
|
90
90
|
```
|
|
91
91
|
Identity: workspaceId
|
|
92
92
|
Lifetime: 2-5+ years
|
|
93
93
|
Boundary: Members added, roles changed, settings updated — for this one workspace
|
|
94
94
|
Watch for: workspace-level settings and individual member activity
|
|
95
|
-
are different concerns — keep member activity in its own
|
|
95
|
+
are different concerns — keep member activity in its own timeline if
|
|
96
96
|
it needs independent access patterns
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
**Data Collection
|
|
99
|
+
**Data Collection Entity**:
|
|
100
100
|
```
|
|
101
101
|
Identity: depends on what's actually being tracked as one thing
|
|
102
102
|
Watch for: this is the domain most likely to hide a Pattern 3
|
|
@@ -104,6 +104,6 @@ Watch for: this is the domain most likely to hide a Pattern 3
|
|
|
104
104
|
points about the same business entity?"
|
|
105
105
|
→ If NO, the boundary is wrong: split by whatever entity each
|
|
106
106
|
data point actually belongs to.
|
|
107
|
-
→ If YES, the boundary is fine even if the
|
|
107
|
+
→ If YES, the boundary is fine even if the timeline accumulates a
|
|
108
108
|
lot of events — that's a volume question, not a boundary one.
|
|
109
109
|
```
|
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Timeline Boundary Design Patterns
|
|
2
2
|
|
|
3
3
|
## Contents
|
|
4
|
-
-
|
|
5
|
-
-
|
|
4
|
+
- Entity Boundary Design Patterns (5 patterns with examples)
|
|
5
|
+
- Timeline Boundary Decision Tree
|
|
6
6
|
- Red Flags: Boundary Is Wrong
|
|
7
7
|
- Tips for Finding the Right Boundary
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Entity Boundary Design Patterns
|
|
12
12
|
|
|
13
13
|
### Pattern 1: Single Entity (Most Common)
|
|
14
14
|
|
|
15
|
-
CORRECT: One
|
|
15
|
+
CORRECT: One timeline boundary per business entity
|
|
16
16
|
```
|
|
17
|
-
|
|
17
|
+
Entity: Order
|
|
18
18
|
Root Identity: orderId (e.g., 'order-123')
|
|
19
|
-
Entity: The specific order
|
|
20
19
|
Lifetime: 1-2 years
|
|
21
20
|
|
|
22
|
-
Events in
|
|
21
|
+
Events in timeline:
|
|
23
22
|
1. OrderCreated (2024-01-15)
|
|
24
23
|
2. OrderLineAdded (2024-01-15)
|
|
25
24
|
3. OrderLineAdded (2024-01-15)
|
|
@@ -30,16 +29,15 @@ Events in stream:
|
|
|
30
29
|
|
|
31
30
|
Identity Principle: orderId is the natural business key
|
|
32
31
|
Boundary: Everything about THIS specific order, nothing else
|
|
33
|
-
Consistency: Only one order being modified at a time
|
|
34
32
|
```
|
|
35
33
|
|
|
36
34
|
---
|
|
37
35
|
|
|
38
36
|
### Pattern 2: Composite Entity (Proper Composition)
|
|
39
37
|
|
|
40
|
-
CORRECT:
|
|
38
|
+
CORRECT: Entity contains related child entities
|
|
41
39
|
```
|
|
42
|
-
|
|
40
|
+
Entity: Order
|
|
43
41
|
Root Identity: orderId (e.g., 'order-456')
|
|
44
42
|
|
|
45
43
|
Contains related children (same lifetime):
|
|
@@ -54,7 +52,7 @@ Contains related children (same lifetime):
|
|
|
54
52
|
- PaymentInfo:
|
|
55
53
|
method: credit_card, amount: $400
|
|
56
54
|
|
|
57
|
-
Events in
|
|
55
|
+
Events in timeline:
|
|
58
56
|
1. OrderCreated (customer-789, 3 items)
|
|
59
57
|
2. OrderLineAdded (item 1)
|
|
60
58
|
3. OrderLineAdded (item 2)
|
|
@@ -65,16 +63,16 @@ Events in stream:
|
|
|
65
63
|
|
|
66
64
|
Pattern: Small, bounded number of children per parent
|
|
67
65
|
Lifetime: Parent and all children created/destroyed together
|
|
68
|
-
|
|
66
|
+
Together: they change as a unit (can't ship without payment, etc.)
|
|
69
67
|
```
|
|
70
68
|
|
|
71
69
|
---
|
|
72
70
|
|
|
73
71
|
### Pattern 3: Collection (ANTI-PATTERN - DO NOT USE)
|
|
74
72
|
|
|
75
|
-
WRONG: Treating a collection as an
|
|
73
|
+
WRONG: Treating a collection as an entity
|
|
76
74
|
```
|
|
77
|
-
Bad
|
|
75
|
+
Bad Entity: AllOrders
|
|
78
76
|
Root Identity: "all-orders-collection" (artificial, meaningless)
|
|
79
77
|
|
|
80
78
|
Contains: Every order ever created
|
|
@@ -87,23 +85,21 @@ Events:
|
|
|
87
85
|
|
|
88
86
|
Problems with this approach:
|
|
89
87
|
- No single business identity (it's a collection, not an entity)
|
|
90
|
-
-
|
|
91
|
-
- Can't split or scale
|
|
92
|
-
- Every write goes to the same stream (contention)
|
|
88
|
+
- Timeline grows unbounded — no natural end to its lifetime
|
|
93
89
|
|
|
94
|
-
Solution: Use a projection/read model query instead, not an
|
|
90
|
+
Solution: Use a projection/read model query instead, not an entity
|
|
95
91
|
- Query: "GetAllOrdersByCustomer(customer-id)"
|
|
96
92
|
- Query: "GetOrdersByStatus(status)"
|
|
97
|
-
- Rebuild from individual Order
|
|
93
|
+
- Rebuild from individual Order timelines on-demand
|
|
98
94
|
```
|
|
99
95
|
|
|
100
96
|
---
|
|
101
97
|
|
|
102
98
|
### Pattern 4: Event Log (ANTI-PATTERN - DO NOT USE)
|
|
103
99
|
|
|
104
|
-
WRONG: Using an
|
|
100
|
+
WRONG: Using an entity as an event log
|
|
105
101
|
```
|
|
106
|
-
Bad
|
|
102
|
+
Bad Entity: SystemLog
|
|
107
103
|
Root Identity: "system-log" (meaningless placeholder)
|
|
108
104
|
|
|
109
105
|
Contains: Every system event imaginable
|
|
@@ -119,23 +115,19 @@ Events:
|
|
|
119
115
|
Problems with this approach:
|
|
120
116
|
- No business identity (log of everything)
|
|
121
117
|
- Events unrelated to each other (mixing user, order, payment, inventory)
|
|
122
|
-
- No consistency boundary (user login != order creation)
|
|
123
118
|
- Can't answer "what's the state of X?" (too mixed)
|
|
124
|
-
- Contention: every subsystem writing to the same stream
|
|
125
|
-
- Can't replay meaningfully (mixed concerns)
|
|
126
119
|
|
|
127
|
-
Solution: Use separate event
|
|
128
|
-
- Keep dedicated event
|
|
129
|
-
- Use a time-series database for metrics/logs, not a domain stream
|
|
120
|
+
Solution: Use separate event timelines per business entity
|
|
121
|
+
- Keep dedicated event timelines: Order, Payment, Inventory, User
|
|
130
122
|
```
|
|
131
123
|
|
|
132
124
|
---
|
|
133
125
|
|
|
134
|
-
### Pattern 5: Historical
|
|
126
|
+
### Pattern 5: Historical Entity (GOOD - When Needed)
|
|
135
127
|
|
|
136
128
|
CORRECT: Keep historical data for audit/compliance as its own boundary
|
|
137
129
|
```
|
|
138
|
-
|
|
130
|
+
Entity: ArchivedOrder
|
|
139
131
|
Root Identity: archivedOrderId (e.g., 'archived-order-001')
|
|
140
132
|
Purpose: Regulatory compliance (7-year retention)
|
|
141
133
|
|
|
@@ -157,8 +149,8 @@ Events:
|
|
|
157
149
|
|
|
158
150
|
Lifetime: 7 years (regulatory requirement)
|
|
159
151
|
|
|
160
|
-
Key
|
|
161
|
-
- Completely separate from the active Order
|
|
152
|
+
Key principles:
|
|
153
|
+
- Completely separate from the active Order entity
|
|
162
154
|
- Active Order is for current business operations
|
|
163
155
|
- Archived Order is an immutable historical record
|
|
164
156
|
- Different access patterns, different lifecycles
|
|
@@ -166,16 +158,16 @@ Key architectural principles:
|
|
|
166
158
|
|
|
167
159
|
---
|
|
168
160
|
|
|
169
|
-
##
|
|
161
|
+
## Timeline Boundary Decision Tree
|
|
170
162
|
|
|
171
|
-
Use this to decide whether a
|
|
163
|
+
Use this to decide whether a timeline is bounded around the right business identity:
|
|
172
164
|
|
|
173
165
|
```
|
|
174
|
-
Does your
|
|
175
|
-
NO → This is not an
|
|
176
|
-
SOLUTION: Use a read model/projection, not an
|
|
166
|
+
Does your timeline have a natural business identity?
|
|
167
|
+
NO → This is not an entity, it's a log/report
|
|
168
|
+
SOLUTION: Use a read model/projection, not an entity
|
|
177
169
|
|
|
178
|
-
YES → Does every event in the
|
|
170
|
+
YES → Does every event in the timeline belong to that one entity's lifecycle?
|
|
179
171
|
|
|
180
172
|
YES → GOOD: Boundary is correctly scoped to one identity
|
|
181
173
|
|
|
@@ -191,28 +183,28 @@ Does your stream have a natural business identity?
|
|
|
191
183
|
|
|
192
184
|
## Red Flags: Boundary Is Wrong
|
|
193
185
|
|
|
194
|
-
If your
|
|
186
|
+
If your timeline exhibits ANY of these, the fix is a narrower or different identity — not a technical workaround:
|
|
195
187
|
|
|
196
188
|
```
|
|
197
|
-
Red Flag 1:
|
|
198
|
-
Cause: The
|
|
189
|
+
Red Flag 1: Timeline growing continuously with no natural end
|
|
190
|
+
Cause: The timeline identity spans an unbounded population, not one entity
|
|
199
191
|
Solution: Re-scope to a single business entity's lifecycle
|
|
200
|
-
Example: "AllOrders"
|
|
192
|
+
Example: "AllOrders" timeline → "Order" per customer order
|
|
201
193
|
|
|
202
194
|
Red Flag 2: Events with no shared business meaning
|
|
203
|
-
Cause: Treating a log as an
|
|
204
|
-
Solution: Use a read model/query instead of an
|
|
195
|
+
Cause: Treating a log as an entity
|
|
196
|
+
Solution: Use a read model/query instead of an entity
|
|
205
197
|
Example: "SystemMetricRecorded" → use a metrics/observability system
|
|
206
198
|
|
|
207
|
-
Red Flag 3:
|
|
208
|
-
Cause:
|
|
209
|
-
Solution: Split into separate
|
|
199
|
+
Red Flag 3: Timeline contains unrelated entities
|
|
200
|
+
Cause: Entity boundary is wrong
|
|
201
|
+
Solution: Split into separate entities
|
|
210
202
|
Example: "AllOrders" → "Order" per customer
|
|
211
203
|
|
|
212
|
-
Red Flag 4: Can't explain what single business question the
|
|
213
|
-
Cause: Not a real
|
|
204
|
+
Red Flag 4: Can't explain what single business question the timeline answers
|
|
205
|
+
Cause: Not a real entity — probably a collection or log
|
|
214
206
|
Solution: Convert to a read model/projection
|
|
215
|
-
Example: "SystemEvents" → query specific per-entity
|
|
207
|
+
Example: "SystemEvents" → query specific per-entity timelines instead
|
|
216
208
|
```
|
|
217
209
|
|
|
218
210
|
---
|
|
@@ -221,7 +213,7 @@ Red Flag 4: Can't explain what single business question the stream answers
|
|
|
221
213
|
|
|
222
214
|
### 1. Anchor on a Single Business Identity
|
|
223
215
|
```
|
|
224
|
-
Every
|
|
216
|
+
Every timeline should answer: "the history of exactly which entity?"
|
|
225
217
|
If the answer is "a category of things" or "everything," the
|
|
226
218
|
boundary is wrong — it's describing a collection, not an entity.
|
|
227
219
|
```
|
|
@@ -248,7 +240,7 @@ UserAccount (everything about a user, several unrelated concerns)
|
|
|
248
240
|
|
|
249
241
|
### 4. Separate Active From Historical Concerns
|
|
250
242
|
```
|
|
251
|
-
Keeping everything in one
|
|
243
|
+
Keeping everything in one entity forever conflates two different
|
|
252
244
|
lifecycles: the entity while it's active, and its record afterward.
|
|
253
245
|
Example:
|
|
254
246
|
- ActiveSubscription (current state)
|