@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.
- package/package.json +1 -1
- package/shared/skills/learn-eventmodelers-api/SKILL.md +22 -13
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +2 -23
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/references/api-fallback.md +11 -0
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +6 -57
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/references/api-fallback.md +68 -0
- package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +4 -61
- package/stacks/modeling-kit/templates/.claude/skills/attributes/references/api-fallback.md +39 -0
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +9 -53
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md +63 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +12 -380
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/references/examples.md +329 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +45 -290
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/api-fallback.md +97 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/examples.md +35 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +15 -410
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/api-fallback.md +22 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/examples.md +397 -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 +148 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/references/api-fallback.md +21 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +48 -448
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/references/examples.md +167 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +31 -380
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +31 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/examples.md +216 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +33 -393
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +79 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/examples.md +282 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +54 -454
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +67 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/examples.md +273 -0
- 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 +37 -184
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +58 -99
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +82 -163
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +86 -78
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +51 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +5 -5
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +10 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +22 -51
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +41 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +12 -9
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +1 -10
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +29 -382
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +77 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/examples.md +271 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +12 -349
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/references/examples.md +306 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +69 -107
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +75 -160
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +3 -110
- package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +118 -0
- package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +5 -25
- package/stacks/modeling-kit/templates/.claude/skills/handle-comment/references/api-fallback.md +35 -0
- package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +9 -44
- package/stacks/modeling-kit/templates/.claude/skills/html-screen/references/api-fallback.md +51 -0
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +34 -180
- package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +203 -0
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +14 -81
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +74 -0
- package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +4 -45
- package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/references/api-fallback.md +44 -0
- package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +19 -88
- package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +91 -0
- package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +1 -9
- package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +0 -674
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +0 -204
|
@@ -1,150 +1,109 @@
|
|
|
1
|
-
# Domain-Specific
|
|
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
|
-
|
|
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
|
|
14
|
+
**Order Entity**:
|
|
14
15
|
```
|
|
15
|
-
|
|
16
|
+
Identity: orderId
|
|
16
17
|
Lifetime: 1-3 years
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
23
|
+
**Shopping Cart Entity**:
|
|
24
24
|
```
|
|
25
|
-
|
|
26
|
-
Lifetime: 30 minutes to 2 years (varies widely)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
33
|
+
**User Account Entity**:
|
|
34
34
|
```
|
|
35
|
-
|
|
35
|
+
Identity: userId
|
|
36
36
|
Lifetime: 5-10+ years
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
47
|
+
**Account Entity**:
|
|
48
48
|
```
|
|
49
|
-
|
|
49
|
+
Identity: accountId
|
|
50
50
|
Lifetime: 10-50+ years
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
57
|
+
**Transaction Entity**:
|
|
59
58
|
```
|
|
60
|
-
|
|
59
|
+
Identity: transactionId
|
|
61
60
|
Lifetime: 1-2 months (then archived)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
66
|
+
**Loan Entity**:
|
|
69
67
|
```
|
|
70
|
-
|
|
68
|
+
Identity: loanId
|
|
71
69
|
Lifetime: 5-30 years
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
80
|
+
**Subscription Entity**:
|
|
84
81
|
```
|
|
85
|
-
|
|
82
|
+
Identity: subscriptionId
|
|
86
83
|
Lifetime: 1-5+ years
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
89
|
+
**User Workspace Entity**:
|
|
94
90
|
```
|
|
95
|
-
|
|
91
|
+
Identity: workspaceId
|
|
96
92
|
Lifetime: 2-5+ years
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
#
|
|
1
|
+
# Timeline Boundary Design Patterns
|
|
2
2
|
|
|
3
3
|
## Contents
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
- Red Flags:
|
|
7
|
-
- Tips for
|
|
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
|
-
##
|
|
11
|
+
## Entity Boundary Design Patterns
|
|
12
12
|
|
|
13
13
|
### Pattern 1: Single Entity (Most Common)
|
|
14
14
|
|
|
15
|
-
|
|
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)
|
|
@@ -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
|
-
|
|
38
|
+
CORRECT: Entity contains related child entities
|
|
44
39
|
```
|
|
45
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
73
|
+
WRONG: Treating a collection as an entity
|
|
82
74
|
```
|
|
83
|
-
Bad
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
-
|
|
100
|
+
WRONG: Using an entity as an event log
|
|
119
101
|
```
|
|
120
|
-
Bad
|
|
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
|
|
148
|
-
- Keep dedicated event
|
|
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
|
|
126
|
+
### Pattern 5: Historical Entity (GOOD - When Needed)
|
|
156
127
|
|
|
157
|
-
|
|
128
|
+
CORRECT: Keep historical data for audit/compliance as its own boundary
|
|
158
129
|
```
|
|
159
|
-
|
|
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:
|
|
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
|
|
184
|
-
- Completely separate from active Order
|
|
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
|
|
155
|
+
- Archived Order is an immutable historical record
|
|
156
|
+
- Different access patterns, different lifecycles
|
|
188
157
|
```
|
|
189
158
|
|
|
190
159
|
---
|
|
191
160
|
|
|
192
|
-
##
|
|
161
|
+
## Timeline Boundary Decision Tree
|
|
193
162
|
|
|
194
|
-
Use this to decide
|
|
163
|
+
Use this to decide whether a timeline is bounded around the right business identity:
|
|
195
164
|
|
|
196
165
|
```
|
|
197
|
-
Does your
|
|
198
|
-
NO → This is not an
|
|
199
|
-
SOLUTION: Use read model/projection, not
|
|
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 →
|
|
170
|
+
YES → Does every event in the timeline belong to that one entity's lifecycle?
|
|
202
171
|
|
|
203
|
-
|
|
204
|
-
PERFECT: No optimization needed
|
|
172
|
+
YES → GOOD: Boundary is correctly scoped to one identity
|
|
205
173
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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:
|
|
184
|
+
## Red Flags: Boundary Is Wrong
|
|
249
185
|
|
|
250
|
-
If your
|
|
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
|
-
|
|
254
|
-
Cause:
|
|
255
|
-
Solution:
|
|
256
|
-
Example: "
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
Cause: Treating log as
|
|
260
|
-
Solution: Use read model/query instead of
|
|
261
|
-
Example: "SystemMetricRecorded"
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
Cause:
|
|
265
|
-
Solution: Split into separate
|
|
266
|
-
Example: "AllOrders"
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
Cause:
|
|
270
|
-
Solution:
|
|
271
|
-
Example:
|
|
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
|
|
212
|
+
## Tips for Finding the Right Boundary
|
|
287
213
|
|
|
288
|
-
### 1.
|
|
214
|
+
### 1. Anchor on a Single Business Identity
|
|
289
215
|
```
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
298
|
-
|
|
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
|
|
229
|
+
### 3. Split When the Identity Is Actually Two Identities
|
|
304
230
|
```
|
|
305
|
-
|
|
306
|
-
Order (per order)
|
|
307
|
-
OrderLine (per line item)
|
|
308
|
-
|
|
309
|
-
|
|
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.
|
|
241
|
+
### 4. Separate Active From Historical Concerns
|
|
316
242
|
```
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
```
|