@eventmodelers/cli 1.0.36 → 1.0.37

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 (69) hide show
  1. package/package.json +1 -1
  2. package/shared/skills/learn-eventmodelers-api/SKILL.md +12 -10
  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 +9 -319
  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 +23 -199
  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 +13 -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-designing-automation-chains/SKILL.md +132 -0
  20. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/references/api-fallback.md +21 -0
  21. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +9 -236
  22. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/references/examples.md +257 -0
  23. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +28 -302
  24. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +31 -0
  25. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/examples.md +216 -0
  26. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +30 -343
  27. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +79 -0
  28. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/examples.md +282 -0
  29. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +51 -400
  30. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +67 -0
  31. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/examples.md +273 -0
  32. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +45 -152
  33. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +49 -90
  34. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +64 -137
  35. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +74 -65
  36. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +51 -0
  37. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +1 -5
  38. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +10 -0
  39. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +19 -36
  40. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +41 -0
  41. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +12 -9
  42. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +1 -10
  43. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +26 -332
  44. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +77 -0
  45. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/examples.md +271 -0
  46. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +9 -294
  47. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/references/examples.md +306 -0
  48. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +12 -11
  49. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +14 -0
  50. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +6 -36
  51. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +14 -0
  52. package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +3 -110
  53. package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +118 -0
  54. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +5 -25
  55. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/references/api-fallback.md +35 -0
  56. package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +9 -44
  57. package/stacks/modeling-kit/templates/.claude/skills/html-screen/references/api-fallback.md +51 -0
  58. package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +23 -183
  59. package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +193 -0
  60. package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +14 -81
  61. package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +74 -0
  62. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +4 -45
  63. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/references/api-fallback.md +44 -0
  64. package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +19 -88
  65. package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +91 -0
  66. package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +1 -9
  67. package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/references/api-fallback.md +14 -0
  68. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +0 -674
  69. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +0 -204
@@ -1,10 +1,11 @@
1
- # Domain-Specific Stream Size Patterns
1
+ # Domain-Specific Stream 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
 
@@ -12,32 +13,31 @@
12
13
 
13
14
  **Order Aggregate**:
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
23
  **Shopping Cart Aggregate**:
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 streams if their behavior diverges
30
+ (e.g. abandoned-cart recovery vs. active checkout flow)
31
31
  ```
32
32
 
33
33
  **User Account Aggregate**:
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
  ---
@@ -46,34 +46,31 @@ Reason: Infrequent events, long lifetime, many separate streams
46
46
 
47
47
  **Account Aggregate**:
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 stream is long-lived; don't
54
+ split it just because it accumulates many events over a long life
56
55
  ```
57
56
 
58
57
  **Transaction Aggregate**:
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 stream, not folded into the account
66
64
  ```
67
65
 
68
66
  **Loan Aggregate**:
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
  ---
@@ -82,69 +79,31 @@ Example: ActiveLoan vs. CompletedLoan aggregates
82
79
 
83
80
  **Subscription Aggregate**:
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
89
  **User Workspace Aggregate**:
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
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 stream if
96
+ it needs independent access patterns
101
97
  ```
102
98
 
103
99
  **Data Collection Aggregate**:
104
100
  ```
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
-
122
- ```
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 stream 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,10 +1,10 @@
1
- # Aggregate Boundary Design Patterns
1
+ # Stream Boundary Design Patterns
2
2
 
3
3
  ## Contents
4
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
5
+ - Stream Boundary Decision Tree
6
+ - Red Flags: Boundary Is Wrong
7
+ - Tips for Finding the Right Boundary
8
8
 
9
9
  ---
10
10
 
@@ -12,7 +12,7 @@
12
12
 
13
13
  ### Pattern 1: Single Entity (Most Common)
14
14
 
15
- CORRECT: One aggregate per entity
15
+ CORRECT: One aggregate per entity
16
16
  ```
17
17
  Aggregate: Order
18
18
  Root Identity: orderId (e.g., 'order-123')
@@ -28,9 +28,6 @@ Events in stream:
28
28
  6. OrderShipped (2024-01-20)
29
29
  7. OrderDelivered (2024-01-25)
30
30
 
31
- Stream Length: 7 events
32
- Snapshotting: NOT NEEDED
33
-
34
31
  Identity Principle: orderId is the natural business key
35
32
  Boundary: Everything about THIS specific order, nothing else
36
33
  Consistency: Only one order being modified at a time
@@ -40,7 +37,7 @@ Consistency: Only one order being modified at a time
40
37
 
41
38
  ### Pattern 2: Composite Entity (Proper Composition)
42
39
 
43
- CORRECT: Aggregate contains related child entities
40
+ CORRECT: Aggregate contains related child entities
44
41
  ```
45
42
  Aggregate: Order
46
43
  Root Identity: orderId (e.g., 'order-456')
@@ -66,9 +63,6 @@ Events in stream:
66
63
  6. PaymentProcessed (authorization complete)
67
64
  7. OrderShipped (tracking 123456)
68
65
 
69
- Stream Length: 7 events
70
- Snapshotting: NOT NEEDED (well under 1000)
71
-
72
66
  Pattern: Small, bounded number of children per parent
73
67
  Lifetime: Parent and all children created/destroyed together
74
68
  Consistency: All modified as a unit (can't ship without payment, etc.)
@@ -78,7 +72,7 @@ Consistency: All modified as a unit (can't ship without payment, etc.)
78
72
 
79
73
  ### Pattern 3: Collection (ANTI-PATTERN - DO NOT USE)
80
74
 
81
- WRONG: Treating a collection as aggregate
75
+ WRONG: Treating a collection as an aggregate
82
76
  ```
83
77
  Bad Aggregate: AllOrders
84
78
  Root Identity: "all-orders-collection" (artificial, meaningless)
@@ -91,19 +85,11 @@ Events:
91
85
  4. OrderCreated (customer-003, order-004)
92
86
  ... (continues forever, unbounded)
93
87
 
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
88
  Problems with this approach:
102
89
  - 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)
90
+ - Stream grows unbounded no natural end to its lifetime
105
91
  - Can't split or scale
106
- - Every write goes to same stream (contention)
92
+ - Every write goes to the same stream (contention)
107
93
 
108
94
  Solution: Use a projection/read model query instead, not an aggregate
109
95
  - Query: "GetAllOrdersByCustomer(customer-id)"
@@ -115,7 +101,7 @@ Solution: Use a projection/read model query instead, not an aggregate
115
101
 
116
102
  ### Pattern 4: Event Log (ANTI-PATTERN - DO NOT USE)
117
103
 
118
- WRONG: Using aggregate as event log
104
+ WRONG: Using an aggregate as an event log
119
105
  ```
120
106
  Bad Aggregate: SystemLog
121
107
  Root Identity: "system-log" (meaningless placeholder)
@@ -130,37 +116,30 @@ Events:
130
116
  6. UserLoggedIn (user-223)
131
117
  ... (grows indefinitely, no pattern)
132
118
 
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
119
  Problems with this approach:
140
120
  - No business identity (log of everything)
141
121
  - Events unrelated to each other (mixing user, order, payment, inventory)
142
122
  - No consistency boundary (user login != order creation)
143
123
  - Can't answer "what's the state of X?" (too mixed)
144
- - Contention: every subsystem writing to same stream
124
+ - Contention: every subsystem writing to the same stream
145
125
  - Can't replay meaningfully (mixed concerns)
146
126
 
147
- Solution: Use separate event logs or time-series database
127
+ Solution: Use separate event streams per business entity
148
128
  - 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
129
+ - Use a time-series database for metrics/logs, not a domain stream
151
130
  ```
152
131
 
153
132
  ---
154
133
 
155
134
  ### Pattern 5: Historical Aggregate (GOOD - When Needed)
156
135
 
157
- CORRECT: Keep historical data for audit/compliance
136
+ CORRECT: Keep historical data for audit/compliance as its own boundary
158
137
  ```
159
138
  Aggregate: ArchivedOrder
160
139
  Root Identity: archivedOrderId (e.g., 'archived-order-001')
161
140
  Purpose: Regulatory compliance (7-year retention)
162
141
 
163
- Contains: Snapshot + audit trail of an order
142
+ Contains: A record + audit trail of an order
164
143
  Events:
165
144
  1. OrderArchived (original order-123 on 2023-12-31)
166
145
  - reason: compliance_retention
@@ -177,153 +156,101 @@ Events:
177
156
  ... (additional audit entries over time)
178
157
 
179
158
  Lifetime: 7 years (regulatory requirement)
180
- Stream Length: 500-2000 events (audit entries added slowly)
181
- Snapshotting: Not needed (historical, not active)
182
159
 
183
160
  Key architectural principles:
184
- - Completely separate from active Order aggregate
161
+ - Completely separate from the active Order aggregate
185
162
  - Active Order is for current business operations
186
- - Archived Order is immutable historical record
187
- - Different access patterns, different SLAs
163
+ - Archived Order is an immutable historical record
164
+ - Different access patterns, different lifecycles
188
165
  ```
189
166
 
190
167
  ---
191
168
 
192
- ## Stream Size Decision Tree
169
+ ## Stream Boundary Decision Tree
193
170
 
194
- Use this to decide if your streams are properly designed:
171
+ Use this to decide whether a stream is bounded around the right business identity:
195
172
 
196
173
  ```
197
174
  Does your stream have a natural business identity?
198
175
  NO → This is not an aggregate, it's a log/report
199
- SOLUTION: Use read model/projection, not aggregate
176
+ SOLUTION: Use a read model/projection, not an aggregate
200
177
 
201
- YES → How many events does it accumulate?
202
-
203
- < 100 events
204
- PERFECT: No optimization needed
178
+ YES → Does every event in the stream belong to that one entity's lifecycle?
205
179
 
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
180
+ YES → GOOD: Boundary is correctly scoped to one identity
214
181
 
215
- 1000-5000 events
216
- Can you split this aggregate?
217
- YES → REDESIGN: Do it now
182
+ NO → The boundary is too wide — it's absorbing events that
183
+ belong to a different entity or a different concern
184
+ REDESIGN: Split by the entity each event actually concerns
218
185
  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
186
+ Order → Order + OrderLineItems (if line items
187
+ have their own independent lifecycle)
244
188
  ```
245
189
 
246
190
  ---
247
191
 
248
- ## Red Flags: Redesign Needed (Not Snapshotting)
192
+ ## Red Flags: Boundary Is Wrong
249
193
 
250
- If your stream exhibits ANY of these, snapshotting won't helpyou need to redesign:
194
+ If your stream exhibits ANY of these, the fix is a narrower or different identity not a technical workaround:
251
195
 
252
196
  ```
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)
197
+ Red Flag 1: Stream growing continuously with no natural end
198
+ Cause: The stream identity spans an unbounded population, not one entity
199
+ Solution: Re-scope to a single business entity's lifecycle
200
+ Example: "AllOrders" stream → "Order" per customer order
257
201
 
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
202
+ Red Flag 2: Events with no shared business meaning
203
+ Cause: Treating a log as an aggregate
204
+ Solution: Use a read model/query instead of an aggregate
205
+ Example: "SystemMetricRecorded" use a metrics/observability system
262
206
 
263
- Red Flag 3: Stream contains unrelated entities
207
+ Red Flag 3: Stream contains unrelated entities
264
208
  Cause: Aggregate boundary is wrong
265
209
  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
210
+ Example: "AllOrders" → "Order" per customer
211
+
212
+ Red Flag 4: Can't explain what single business question the stream answers
213
+ Cause: Not a real aggregate — probably a collection or log
214
+ Solution: Convert to a read model/projection
215
+ Example: "SystemEvents" query specific per-entity streams instead
282
216
  ```
283
217
 
284
218
  ---
285
219
 
286
- ## Tips for Optimal Stream Design
220
+ ## Tips for Finding the Right Boundary
287
221
 
288
- ### 1. Favor Redesign Over Snapshotting
222
+ ### 1. Anchor on a Single Business Identity
289
223
  ```
290
- Cost: Redesign effort < Snapshotting maintenance
291
- Quality: Better design > Better optimization
292
- Future: Smaller streams are easier to scale
224
+ Every stream should answer: "the history of exactly which entity?"
225
+ If the answer is "a category of things" or "everything," the
226
+ boundary is wrong it's describing a collection, not an entity.
293
227
  ```
294
228
 
295
229
  ### 2. Understand Event Granularity
296
230
  ```
297
- RIGHT: One event per meaningful state change
298
- WRONG: Multiple events per semantic operation
231
+ RIGHT: One event per meaningful state change
232
+ WRONG: Multiple events per semantic operation
299
233
  Example: "UserUpdatedProfile" (1 event)
300
234
  NOT: "FirstNameChanged", "LastNameChanged", ... (N events)
301
235
  ```
302
236
 
303
- ### 3. Split When Possible
237
+ ### 3. Split When the Identity Is Actually Two Identities
304
238
  ```
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)
239
+ AllOrders (growing unbounded, no single identity)
240
+ Order (per order)
241
+ OrderLine (per line item, if it has its own independent lifecycle)
242
+
243
+ UserAccount (everything about a user, several unrelated concerns)
244
+ UserProfile (personal info)
245
+ UserPreferences (settings)
246
+ UserSessions (login history)
313
247
  ```
314
248
 
315
- ### 4. Archive Old Data
249
+ ### 4. Separate Active From Historical Concerns
316
250
  ```
317
- Keep everything in active aggregate
318
- Move completed/closed data to archive aggregate
251
+ Keeping everything in one aggregate forever conflates two different
252
+ lifecycles: the entity while it's active, and its record afterward.
319
253
  Example:
320
254
  - ActiveSubscription (current state)
321
255
  - ArchivedSubscription (after cancelled)
322
256
  ```
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
- ```