@eventmodelers/cli 1.0.36 → 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.
Files changed (71) hide show
  1. package/package.json +1 -1
  2. package/shared/skills/learn-eventmodelers-api/SKILL.md +22 -13
  3. package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +2 -23
  4. package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/references/api-fallback.md +11 -0
  5. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +6 -57
  6. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/references/api-fallback.md +68 -0
  7. package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +4 -61
  8. package/stacks/modeling-kit/templates/.claude/skills/attributes/references/api-fallback.md +39 -0
  9. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +9 -53
  10. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md +63 -0
  11. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +12 -380
  12. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/references/examples.md +329 -0
  13. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +45 -290
  14. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/api-fallback.md +97 -0
  15. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/examples.md +35 -0
  16. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +15 -410
  17. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/api-fallback.md +22 -0
  18. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/examples.md +397 -0
  19. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-core-rules/SKILL.md +182 -0
  20. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/SKILL.md +148 -0
  21. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/references/api-fallback.md +21 -0
  22. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +48 -448
  23. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/references/examples.md +167 -0
  24. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +31 -380
  25. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +31 -0
  26. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/examples.md +216 -0
  27. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +33 -393
  28. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +79 -0
  29. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/examples.md +282 -0
  30. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +54 -454
  31. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +67 -0
  32. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/examples.md +273 -0
  33. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-interview-protocol/SKILL.md +31 -0
  34. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +37 -184
  35. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +58 -99
  36. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +82 -163
  37. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +86 -78
  38. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +51 -0
  39. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +5 -5
  40. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +10 -0
  41. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +22 -51
  42. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +41 -0
  43. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +12 -9
  44. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +1 -10
  45. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +29 -382
  46. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +77 -0
  47. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/examples.md +271 -0
  48. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +12 -349
  49. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/references/examples.md +306 -0
  50. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +69 -107
  51. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +14 -0
  52. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +75 -160
  53. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +14 -0
  54. package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +3 -110
  55. package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +118 -0
  56. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +5 -25
  57. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/references/api-fallback.md +35 -0
  58. package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +9 -44
  59. package/stacks/modeling-kit/templates/.claude/skills/html-screen/references/api-fallback.md +51 -0
  60. package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +34 -180
  61. package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +203 -0
  62. package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +14 -81
  63. package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +74 -0
  64. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +4 -45
  65. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/references/api-fallback.md +44 -0
  66. package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +19 -88
  67. package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +91 -0
  68. package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +1 -9
  69. package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/references/api-fallback.md +14 -0
  70. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +0 -674
  71. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +0 -204
@@ -1,150 +1,109 @@
1
- # Domain-Specific Stream Size Patterns
1
+ # Domain-Specific Timeline Boundary Patterns
2
2
 
3
3
  ## Contents
4
4
  - E-commerce patterns
5
5
  - Banking patterns
6
6
  - SaaS patterns
7
- - Implementation checklist
7
+
8
+ Each entry shows a well-scoped boundary for that domain: what identity it's anchored on, and why that identity — not a category or a time window — is the right one.
8
9
 
9
10
  ---
10
11
 
11
12
  ## E-commerce Domain
12
13
 
13
- **Order Aggregate**:
14
+ **Order Entity**:
14
15
  ```
15
- Events: 5-20
16
+ Identity: orderId
16
17
  Lifetime: 1-3 years
17
- Frequency: 1 event per few days
18
- Stream Length: 8-60 events
19
- Snapshotting: NOT NEEDED
20
- Reason: Short entity lifetime, low frequency, few state changes
18
+ Boundary: Everything that happens to this one order
19
+ Reason for this boundary: the order has a clear owner (one customer,
20
+ one checkout) and a natural end (delivered, cancelled, refunded)
21
21
  ```
22
22
 
23
- **Shopping Cart Aggregate**:
23
+ **Shopping Cart Entity**:
24
24
  ```
25
- Events: 5-50+ (add/remove items many times)
26
- Lifetime: 30 minutes to 2 years (varies widely)
27
- Frequency: 1-10 events per hour (if active)
28
- Stream Length: 10-500+ events (depends on user behavior)
29
- Snapshotting: RARELY (only for frequent shoppers)
30
- Strategy: Split abandoned vs. active carts if too long
25
+ Identity: cartId (or customerId, if a customer has exactly one active cart)
26
+ Lifetime: 30 minutes to 2 years (varies widely by product)
27
+ Boundary: Items added/removed for this one cart
28
+ Watch for: an "abandoned" cart and an "active" cart are different
29
+ lifecycles split into separate timelines if their behavior diverges
30
+ (e.g. abandoned-cart recovery vs. active checkout flow)
31
31
  ```
32
32
 
33
- **User Account Aggregate**:
33
+ **User Account Entity**:
34
34
  ```
35
- Events: 2-10 per year (profile updates, settings changes)
35
+ Identity: userId
36
36
  Lifetime: 5-10+ years
37
- Frequency: Very low (events measured in months apart)
38
- Stream Length: 10-100 events
39
- Snapshotting: NOT NEEDED
40
- Reason: Infrequent events, long lifetime, many separate streams
37
+ Boundary: Profile and account-level state for this one user
38
+ Watch for: account state, preferences, and session history are three
39
+ different concerns with different change frequency — split them
40
+ (UserProfile / UserPreferences / UserSessions) rather than bundling
41
41
  ```
42
42
 
43
43
  ---
44
44
 
45
45
  ## Banking Domain
46
46
 
47
- **Account Aggregate**:
47
+ **Account Entity**:
48
48
  ```
49
- Events: 50-500+ per year (deposits, withdrawals, fees)
49
+ Identity: accountId
50
50
  Lifetime: 10-50+ years
51
- Frequency: 0.1-2 events per day
52
- Stream Length: 500-25,000+ events
53
- Snapshotting: MAYBE (at 5000+)
54
- Strategy: Consider splitting by time period or account type
55
- Alternative: Snapshotting might be justified for regulatory access needs
51
+ Boundary: Deposits, withdrawals, and fees for this one account
52
+ Watch for: an account spanning decades is still one identity — the
53
+ boundary is correct even though the timeline is long-lived; don't
54
+ split it just because it accumulates many events over a long life
56
55
  ```
57
56
 
58
- **Transaction Aggregate**:
57
+ **Transaction Entity**:
59
58
  ```
60
- Events: 1-5 (Requested → Processing → Settled)
59
+ Identity: transactionId
61
60
  Lifetime: 1-2 months (then archived)
62
- Frequency: Single transaction, short lifecycle
63
- Stream Length: 2-5 events
64
- Snapshotting: NEVER NEEDED
65
- Reason: Tiny, immutable after completion
61
+ Boundary: Requested → Processing → Settled, for this one transaction
62
+ Reason for this boundary: a transaction is a short, self-contained
63
+ lifecycle treat it as its own timeline, not folded into the account
66
64
  ```
67
65
 
68
- **Loan Aggregate**:
66
+ **Loan Entity**:
69
67
  ```
70
- Events: 100-500+ (payments, rate changes, modifications)
68
+ Identity: loanId
71
69
  Lifetime: 5-30 years
72
- Frequency: 1-5 events per month
73
- Stream Length: 1000-10,000+ events
74
- Snapshotting: CONSIDER AT 5000
75
- Strategy: Split by loan product, payment period, or status
76
- Example: ActiveLoan vs. CompletedLoan aggregates
70
+ Boundary: Payments, rate changes, and modifications for this one loan
71
+ Watch for: "active loan" and "completed loan" are different
72
+ lifecycles with different access patterns — consider splitting into
73
+ ActiveLoan vs. CompletedLoan if their consumers genuinely differ
77
74
  ```
78
75
 
79
76
  ---
80
77
 
81
78
  ## SaaS Domain
82
79
 
83
- **Subscription Aggregate**:
80
+ **Subscription Entity**:
84
81
  ```
85
- Events: 2-20 (Created, Upgraded, Downgraded, Cancelled)
82
+ Identity: subscriptionId
86
83
  Lifetime: 1-5+ years
87
- Frequency: 1-5 events per year
88
- Stream Length: 5-100 events
89
- Snapshotting: NOT NEEDED
90
- Reason: Low frequency, well-defined lifecycle
84
+ Boundary: Created, Upgraded, Downgraded, Cancelled — for this one subscription
85
+ Reason for this boundary: a subscription has one clear owner and a
86
+ well-defined lifecycle
91
87
  ```
92
88
 
93
- **User Workspace Aggregate**:
89
+ **User Workspace Entity**:
94
90
  ```
95
- Events: 10-100+ (members added, roles changed, settings updated)
91
+ Identity: workspaceId
96
92
  Lifetime: 2-5+ years
97
- Frequency: 0.5-5 events per month
98
- Stream Length: 10-500 events
99
- Snapshotting: NOT NEEDED
100
- Reason: Moderate frequency, small discrete events
101
- ```
102
-
103
- **Data Collection Aggregate**:
93
+ Boundary: Members added, roles changed, settings updated — for this one workspace
94
+ Watch for: workspace-level settings and individual member activity
95
+ are different concerns — keep member activity in its own timeline if
96
+ it needs independent access patterns
104
97
  ```
105
- Events: 100-10,000+ (data points added, processed, analyzed)
106
- Lifetime: 1-5+ years
107
- Frequency: 1-1000+ events per day (varies wildly)
108
- Stream Length: 100-50,000+ events
109
- Snapshotting: PROBABLY
110
- Strategy: Split by data type, time period, or processing stage
111
- Question: Are all these events about the same business entity?
112
- → If NO, split the aggregate
113
- → If YES, snapshotting might be needed
114
- ```
115
-
116
- ---
117
-
118
- ## Implementation Checklist
119
-
120
- Before implementing snapshotting, answer ALL of these:
121
98
 
99
+ **Data Collection Entity**:
122
100
  ```
123
- Design Questions:
124
- [ ] Does this aggregate have a single business identity?
125
- [ ] Can I split this into smaller aggregates?
126
- [ ] Are there natural lifecycle phases (archived vs. active)?
127
- [ ] Is event granularity appropriate (not too fine)?
128
-
129
- Performance Questions:
130
- [ ] Have I measured replay latency?
131
- [ ] Does latency exceed acceptable threshold?
132
- [ ] Is the problem snaphotting will solve?
133
- [ ] Or is it a design problem?
134
-
135
- Cost-Benefit Questions:
136
- [ ] How many writes per second?
137
- [ ] How many reads per second?
138
- [ ] What's the read latency requirement (SLA)?
139
- [ ] Is snapshotting complexity worth the benefit?
140
-
141
- Operational Questions:
142
- [ ] How will I version snapshots?
143
- [ ] How will I test snapshot recovery?
144
- [ ] How will I monitor snapshot health?
145
- [ ] Can I implement this given current skills?
101
+ Identity: depends on what's actually being tracked as one thing
102
+ Watch for: this is the domain most likely to hide a Pattern 3
103
+ ("Collection") anti-pattern ask explicitly: "are all these data
104
+ points about the same business entity?"
105
+ If NO, the boundary is wrong: split by whatever entity each
106
+ data point actually belongs to.
107
+ If YES, the boundary is fine even if the timeline accumulates a
108
+ lot of events that's a volume question, not a boundary one.
146
109
  ```
147
-
148
- **If ANY question suggests redesign is better**: Redesign first, snapshot never.
149
-
150
- **If ALL questions support snapshotting**: Proceed with implementation.
@@ -1,25 +1,24 @@
1
- # Aggregate Boundary Design Patterns
1
+ # Timeline Boundary Design Patterns
2
2
 
3
3
  ## Contents
4
- - Aggregate Boundary Design Patterns (5 patterns with examples)
5
- - Stream Size Decision Tree
6
- - Red Flags: Redesign Needed
7
- - Tips for Optimal Stream Design
4
+ - Entity Boundary Design Patterns (5 patterns with examples)
5
+ - Timeline Boundary Decision Tree
6
+ - Red Flags: Boundary Is Wrong
7
+ - Tips for Finding the Right Boundary
8
8
 
9
9
  ---
10
10
 
11
- ## Aggregate Boundary Design Patterns
11
+ ## Entity Boundary Design Patterns
12
12
 
13
13
  ### Pattern 1: Single Entity (Most Common)
14
14
 
15
- CORRECT: One aggregate per entity
15
+ CORRECT: One timeline boundary per business entity
16
16
  ```
17
- Aggregate: Order
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 stream:
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)
@@ -28,21 +27,17 @@ Events in stream:
28
27
  6. OrderShipped (2024-01-20)
29
28
  7. OrderDelivered (2024-01-25)
30
29
 
31
- Stream Length: 7 events
32
- Snapshotting: NOT NEEDED
33
-
34
30
  Identity Principle: orderId is the natural business key
35
31
  Boundary: Everything about THIS specific order, nothing else
36
- Consistency: Only one order being modified at a time
37
32
  ```
38
33
 
39
34
  ---
40
35
 
41
36
  ### Pattern 2: Composite Entity (Proper Composition)
42
37
 
43
- CORRECT: Aggregate contains related child entities
38
+ CORRECT: Entity contains related child entities
44
39
  ```
45
- Aggregate: Order
40
+ Entity: Order
46
41
  Root Identity: orderId (e.g., 'order-456')
47
42
 
48
43
  Contains related children (same lifetime):
@@ -57,7 +52,7 @@ Contains related children (same lifetime):
57
52
  - PaymentInfo:
58
53
  method: credit_card, amount: $400
59
54
 
60
- Events in stream:
55
+ Events in timeline:
61
56
  1. OrderCreated (customer-789, 3 items)
62
57
  2. OrderLineAdded (item 1)
63
58
  3. OrderLineAdded (item 2)
@@ -66,21 +61,18 @@ Events in stream:
66
61
  6. PaymentProcessed (authorization complete)
67
62
  7. OrderShipped (tracking 123456)
68
63
 
69
- Stream Length: 7 events
70
- Snapshotting: NOT NEEDED (well under 1000)
71
-
72
64
  Pattern: Small, bounded number of children per parent
73
65
  Lifetime: Parent and all children created/destroyed together
74
- Consistency: All modified as a unit (can't ship without payment, etc.)
66
+ Together: they change as a unit (can't ship without payment, etc.)
75
67
  ```
76
68
 
77
69
  ---
78
70
 
79
71
  ### Pattern 3: Collection (ANTI-PATTERN - DO NOT USE)
80
72
 
81
- WRONG: Treating a collection as aggregate
73
+ WRONG: Treating a collection as an entity
82
74
  ```
83
- Bad Aggregate: AllOrders
75
+ Bad Entity: AllOrders
84
76
  Root Identity: "all-orders-collection" (artificial, meaningless)
85
77
 
86
78
  Contains: Every order ever created
@@ -91,33 +83,23 @@ Events:
91
83
  4. OrderCreated (customer-003, order-004)
92
84
  ... (continues forever, unbounded)
93
85
 
94
- Month 1: 50,000 events
95
- Year 1: 600,000 events
96
- Year 5: 3,000,000 events
97
-
98
- Stream Length: 1,000,000+ events
99
- Snapshotting: Doesn't help - design is fundamentally wrong
100
-
101
86
  Problems with this approach:
102
87
  - No single business identity (it's a collection, not an entity)
103
- - Stream grows unbounded (can never achieve performance SLA)
104
- - Snapshotting won't fix it (snapshot is also 1M+ events)
105
- - Can't split or scale
106
- - Every write goes to same stream (contention)
88
+ - Timeline grows unbounded no natural end to its lifetime
107
89
 
108
- Solution: Use a projection/read model query instead, not an aggregate
90
+ Solution: Use a projection/read model query instead, not an entity
109
91
  - Query: "GetAllOrdersByCustomer(customer-id)"
110
92
  - Query: "GetOrdersByStatus(status)"
111
- - Rebuild from individual Order streams on-demand
93
+ - Rebuild from individual Order timelines on-demand
112
94
  ```
113
95
 
114
96
  ---
115
97
 
116
98
  ### Pattern 4: Event Log (ANTI-PATTERN - DO NOT USE)
117
99
 
118
- WRONG: Using aggregate as event log
100
+ WRONG: Using an entity as an event log
119
101
  ```
120
- Bad Aggregate: SystemLog
102
+ Bad Entity: SystemLog
121
103
  Root Identity: "system-log" (meaningless placeholder)
122
104
 
123
105
  Contains: Every system event imaginable
@@ -130,37 +112,26 @@ Events:
130
112
  6. UserLoggedIn (user-223)
131
113
  ... (grows indefinitely, no pattern)
132
114
 
133
- Per Day: 100,000+ events
134
- Per Year: 36,500,000+ events
135
-
136
- Stream Length: 10,000,000+ events
137
- Snapshotting: Impossible - design is fundamentally broken
138
-
139
115
  Problems with this approach:
140
116
  - No business identity (log of everything)
141
117
  - Events unrelated to each other (mixing user, order, payment, inventory)
142
- - No consistency boundary (user login != order creation)
143
118
  - Can't answer "what's the state of X?" (too mixed)
144
- - Contention: every subsystem writing to same stream
145
- - Can't replay meaningfully (mixed concerns)
146
119
 
147
- Solution: Use separate event logs or time-series database
148
- - Keep dedicated event streams: Order, Payment, Inventory, User
149
- - Use time-series DB for metrics/logs: Prometheus, DataDog, ELK
150
- - Query system logs separately from domain events
120
+ Solution: Use separate event timelines per business entity
121
+ - Keep dedicated event timelines: Order, Payment, Inventory, User
151
122
  ```
152
123
 
153
124
  ---
154
125
 
155
- ### Pattern 5: Historical Aggregate (GOOD - When Needed)
126
+ ### Pattern 5: Historical Entity (GOOD - When Needed)
156
127
 
157
- CORRECT: Keep historical data for audit/compliance
128
+ CORRECT: Keep historical data for audit/compliance as its own boundary
158
129
  ```
159
- Aggregate: ArchivedOrder
130
+ Entity: ArchivedOrder
160
131
  Root Identity: archivedOrderId (e.g., 'archived-order-001')
161
132
  Purpose: Regulatory compliance (7-year retention)
162
133
 
163
- Contains: Snapshot + audit trail of an order
134
+ Contains: A record + audit trail of an order
164
135
  Events:
165
136
  1. OrderArchived (original order-123 on 2023-12-31)
166
137
  - reason: compliance_retention
@@ -177,153 +148,101 @@ Events:
177
148
  ... (additional audit entries over time)
178
149
 
179
150
  Lifetime: 7 years (regulatory requirement)
180
- Stream Length: 500-2000 events (audit entries added slowly)
181
- Snapshotting: Not needed (historical, not active)
182
151
 
183
- Key architectural principles:
184
- - Completely separate from active Order aggregate
152
+ Key principles:
153
+ - Completely separate from the active Order entity
185
154
  - Active Order is for current business operations
186
- - Archived Order is immutable historical record
187
- - Different access patterns, different SLAs
155
+ - Archived Order is an immutable historical record
156
+ - Different access patterns, different lifecycles
188
157
  ```
189
158
 
190
159
  ---
191
160
 
192
- ## Stream Size Decision Tree
161
+ ## Timeline Boundary Decision Tree
193
162
 
194
- Use this to decide if your streams are properly designed:
163
+ Use this to decide whether a timeline is bounded around the right business identity:
195
164
 
196
165
  ```
197
- Does your stream have a natural business identity?
198
- NO → This is not an aggregate, it's a log/report
199
- SOLUTION: Use read model/projection, not aggregate
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
200
169
 
201
- YES → How many events does it accumulate?
170
+ YES → Does every event in the timeline belong to that one entity's lifecycle?
202
171
 
203
- < 100 events
204
- PERFECT: No optimization needed
172
+ YES GOOD: Boundary is correctly scoped to one identity
205
173
 
206
- 100-1000 events
207
- Is it growing because of high frequency?
208
- NO → GOOD: Probably well-designed
209
- YES → MONITOR: Watch for growth
210
-
211
- Does each event represent a meaningful state change?
212
- YES → GOOD: Healthy stream
213
- NO → REDESIGN: Too granular events
214
-
215
- 1000-5000 events
216
- Can you split this aggregate?
217
- YES → REDESIGN: Do it now
174
+ NO → The boundary is too wide — it's absorbing events that
175
+ belong to a different entity or a different concern
176
+ REDESIGN: Split by the entity each event actually concerns
218
177
  Examples: User → UserProfile + UserSessions
219
- Order → Order + OrderLineItems
220
-
221
- NO → Is read frequency high (> 10/sec)?
222
- YES → Consider snapshotting at 5000
223
- NO → ACCEPTABLE: Leave as-is
224
-
225
- Is latency critical (< 100ms)?
226
- YES → MONITOR: Measure replay time
227
- NO → ACCEPTABLE: No snapshotting needed
228
-
229
- 5000-10000 events
230
- This is a design problem → REDESIGN
231
- OR business justifies complexity → Snapshot at 5000
232
-
233
- Questions before snapshotting:
234
- Can I split aggregate? (usually YES)
235
- Can I reduce event granularity? (sometimes)
236
- Am I using a read model for this aggregate? (maybe not)
237
- If all NO → Then snapshot is justified
238
-
239
- > 10000 events
240
- CRITICAL: Redesign required
241
- This is NOT a properly designed aggregate
242
- Snapshotting won't save you
243
- Root cause: Aggregate boundary is wrong
178
+ Order → Order + OrderLineItems (if line items
179
+ have their own independent lifecycle)
244
180
  ```
245
181
 
246
182
  ---
247
183
 
248
- ## Red Flags: Redesign Needed (Not Snapshotting)
184
+ ## Red Flags: Boundary Is Wrong
249
185
 
250
- If your stream exhibits ANY of these, snapshotting won't helpyou need to redesign:
186
+ If your timeline exhibits ANY of these, the fix is a narrower or different identity not a technical workaround:
251
187
 
252
188
  ```
253
- Red Flag 1: Stream growing > 1000 events/day
254
- Cause: Events are too granular
255
- Solution: Batch events or coarsen granularity
256
- Example: "UserClickedButton" → "UserCompletedTask" (higher level)
257
-
258
- Red Flag 2: Thousands of events but no business meaning
259
- Cause: Treating log as aggregate
260
- Solution: Use read model/query instead of aggregate
261
- Example: "SystemMetricRecorded" Use time-series database
262
-
263
- Red Flag 3: Stream contains unrelated entities
264
- Cause: Aggregate boundary is wrong
265
- Solution: Split into separate aggregates
266
- Example: "AllOrders" → "Order" per customer
267
-
268
- Red Flag 4: Snapshot is 80% of the stream size
269
- Cause: Snapshot isn't helping
270
- Solution: Re-examine aggregate boundary
271
- Example: If snapshot is 800 events and deltas 100, redesign
272
-
273
- Red Flag 5: Can't explain what business question the stream answers
274
- Cause: Not a real aggregate
275
- Solution: Convert to read model/projection
276
- Example: "SystemEvents" → Query specific streams
277
-
278
- Red Flag 6: Stream length doubles every 6 months
279
- Cause: Exponential growth pattern
280
- Solution: Likely aggregate boundary issue
281
- Example: Split by time period: 2024-Orders vs. 2025-Orders
189
+ Red Flag 1: Timeline growing continuously with no natural end
190
+ Cause: The timeline identity spans an unbounded population, not one entity
191
+ Solution: Re-scope to a single business entity's lifecycle
192
+ Example: "AllOrders" timeline → "Order" per customer order
193
+
194
+ Red Flag 2: Events with no shared business meaning
195
+ Cause: Treating a log as an entity
196
+ Solution: Use a read model/query instead of an entity
197
+ Example: "SystemMetricRecorded" use a metrics/observability system
198
+
199
+ Red Flag 3: Timeline contains unrelated entities
200
+ Cause: Entity boundary is wrong
201
+ Solution: Split into separate entities
202
+ Example: "AllOrders" → "Order" per customer
203
+
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
206
+ Solution: Convert to a read model/projection
207
+ Example: "SystemEvents" query specific per-entity timelines instead
282
208
  ```
283
209
 
284
210
  ---
285
211
 
286
- ## Tips for Optimal Stream Design
212
+ ## Tips for Finding the Right Boundary
287
213
 
288
- ### 1. Favor Redesign Over Snapshotting
214
+ ### 1. Anchor on a Single Business Identity
289
215
  ```
290
- Cost: Redesign effort < Snapshotting maintenance
291
- Quality: Better design > Better optimization
292
- Future: Smaller streams are easier to scale
216
+ Every timeline should answer: "the history of exactly which entity?"
217
+ If the answer is "a category of things" or "everything," the
218
+ boundary is wrong it's describing a collection, not an entity.
293
219
  ```
294
220
 
295
221
  ### 2. Understand Event Granularity
296
222
  ```
297
- RIGHT: One event per meaningful state change
298
- WRONG: Multiple events per semantic operation
223
+ RIGHT: One event per meaningful state change
224
+ WRONG: Multiple events per semantic operation
299
225
  Example: "UserUpdatedProfile" (1 event)
300
226
  NOT: "FirstNameChanged", "LastNameChanged", ... (N events)
301
227
  ```
302
228
 
303
- ### 3. Split When Possible
229
+ ### 3. Split When the Identity Is Actually Two Identities
304
230
  ```
305
- AllOrders (growing unbounded)
306
- Order (per order)
307
- OrderLine (per line item)
308
-
309
- UserAccount (everything about user)
310
- UserProfile (personal info)
311
- UserPreferences (settings)
312
- UserSessions (login history)
231
+ AllOrders (growing unbounded, no single identity)
232
+ Order (per order)
233
+ OrderLine (per line item, if it has its own independent lifecycle)
234
+
235
+ UserAccount (everything about a user, several unrelated concerns)
236
+ UserProfile (personal info)
237
+ UserPreferences (settings)
238
+ UserSessions (login history)
313
239
  ```
314
240
 
315
- ### 4. Archive Old Data
241
+ ### 4. Separate Active From Historical Concerns
316
242
  ```
317
- Keep everything in active aggregate
318
- Move completed/closed data to archive aggregate
243
+ Keeping everything in one entity forever conflates two different
244
+ lifecycles: the entity while it's active, and its record afterward.
319
245
  Example:
320
246
  - ActiveSubscription (current state)
321
247
  - ArchivedSubscription (after cancelled)
322
248
  ```
323
-
324
- ### 5. Measure Before Optimizing
325
- ```
326
- Assume snapshotting is needed
327
- Measure replay latency first
328
- Only snapshot if measurement justifies it
329
- ```