@eventmodelers/cli 0.0.1

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 (220) hide show
  1. package/README.md +175 -0
  2. package/cli.js +676 -0
  3. package/package.json +35 -0
  4. package/shared/build-kit/code-export.mjs +560 -0
  5. package/shared/build-kit/lib/ollama-agent.js +147 -0
  6. package/shared/build-kit/package.json +11 -0
  7. package/shared/build-kit/ralph-ollama.js +39 -0
  8. package/shared/build-kit/realtime-agent.js +18 -0
  9. package/stacks/axon/templates/.claude/skills/build-automation/SKILL.md +407 -0
  10. package/stacks/axon/templates/.claude/skills/build-automation/references/feature-flag-patterns.md +114 -0
  11. package/stacks/axon/templates/.claude/skills/build-automation-workflow/SKILL.md +496 -0
  12. package/stacks/axon/templates/.claude/skills/build-automation-workflow/references/axon-workflow-api.md +279 -0
  13. package/stacks/axon/templates/.claude/skills/build-automation-workflow/references/feature-flag-patterns.md +114 -0
  14. package/stacks/axon/templates/.claude/skills/build-state-change/SKILL.md +396 -0
  15. package/stacks/axon/templates/.claude/skills/build-state-change/references/axon-test-fixture-patterns.md +162 -0
  16. package/stacks/axon/templates/.claude/skills/build-state-change/references/feature-flag-patterns.md +56 -0
  17. package/stacks/axon/templates/.claude/skills/build-state-change/references/rest-api-patterns.md +115 -0
  18. package/stacks/axon/templates/.claude/skills/build-state-view/SKILL.md +282 -0
  19. package/stacks/axon/templates/.claude/skills/connect/SKILL.md +178 -0
  20. package/stacks/axon/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +611 -0
  21. package/stacks/axon/templates/.claude/skills/load-slice/SKILL.md +141 -0
  22. package/stacks/axon/templates/.claude/skills/update-slice-status/SKILL.md +105 -0
  23. package/stacks/axon/templates/build-kit/lib/AGENT.md +47 -0
  24. package/stacks/axon/templates/build-kit/lib/agent.sh +20 -0
  25. package/stacks/axon/templates/build-kit/lib/backend-prompt.md +158 -0
  26. package/stacks/axon/templates/build-kit/lib/prompt.md +126 -0
  27. package/stacks/axon/templates/build-kit/lib/ralph.js +369 -0
  28. package/stacks/axon/templates/build-kit/ralph-claude.js +44 -0
  29. package/stacks/axon/templates/build-kit/ralph.sh +98 -0
  30. package/stacks/axon/templates/root/.env.example +5 -0
  31. package/stacks/axon/templates/root/CLAUDE.md +60 -0
  32. package/stacks/axon/templates/root/README.md +44 -0
  33. package/stacks/axon/templates/root/docker-compose.yml +64 -0
  34. package/stacks/axon/templates/root/mvnw +259 -0
  35. package/stacks/axon/templates/root/mvnw.cmd +149 -0
  36. package/stacks/axon/templates/root/pom.xml +125 -0
  37. package/stacks/axon/templates/root/src/main/java/io/axoniq/quickstart/QuickstartApplication.java +128 -0
  38. package/stacks/axon/templates/root/src/main/java/io/axoniq/quickstart/config/WebConfig.java +155 -0
  39. package/stacks/axon/templates/root/src/main/resources/application.properties +2 -0
  40. package/stacks/axon/templates/root/src/main/resources/static/bg-body.png +0 -0
  41. package/stacks/axon/templates/root/src/main/resources/static/bg-header.png +0 -0
  42. package/stacks/axon/templates/root/src/main/resources/static/index.html +596 -0
  43. package/stacks/cratis-csharp/templates/.claude/skills/_shared/cratis-conventions.md +251 -0
  44. package/stacks/cratis-csharp/templates/.claude/skills/build-automation/SKILL.md +99 -0
  45. package/stacks/cratis-csharp/templates/.claude/skills/build-automation/references/patterns.md +115 -0
  46. package/stacks/cratis-csharp/templates/.claude/skills/build-state-change/SKILL.md +163 -0
  47. package/stacks/cratis-csharp/templates/.claude/skills/build-state-change/references/patterns.md +234 -0
  48. package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/SKILL.md +120 -0
  49. package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/references/patterns.md +166 -0
  50. package/stacks/cratis-csharp/templates/.claude/skills/connect/SKILL.md +169 -0
  51. package/stacks/cratis-csharp/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +609 -0
  52. package/stacks/cratis-csharp/templates/.claude/skills/load-slice/SKILL.md +141 -0
  53. package/stacks/cratis-csharp/templates/.claude/skills/update-slice-status/SKILL.md +105 -0
  54. package/stacks/cratis-csharp/templates/build-kit/lib/AGENT.md +58 -0
  55. package/stacks/cratis-csharp/templates/build-kit/lib/agent.sh +20 -0
  56. package/stacks/cratis-csharp/templates/build-kit/lib/backend-prompt.md +129 -0
  57. package/stacks/cratis-csharp/templates/build-kit/lib/prompt.md +124 -0
  58. package/stacks/cratis-csharp/templates/build-kit/lib/ralph.js +302 -0
  59. package/stacks/cratis-csharp/templates/build-kit/ralph-claude.js +37 -0
  60. package/stacks/cratis-csharp/templates/build-kit/ralph.sh +98 -0
  61. package/stacks/cratis-csharp/templates/root/.frontend/index.css +29 -0
  62. package/stacks/cratis-csharp/templates/root/.frontend/index.html +17 -0
  63. package/stacks/cratis-csharp/templates/root/.frontend/main.tsx +18 -0
  64. package/stacks/cratis-csharp/templates/root/.frontend/tsconfig.json +42 -0
  65. package/stacks/cratis-csharp/templates/root/.frontend/tsconfig.node.json +11 -0
  66. package/stacks/cratis-csharp/templates/root/.frontend/vite.config.ts +56 -0
  67. package/stacks/cratis-csharp/templates/root/App.tsx +23 -0
  68. package/stacks/cratis-csharp/templates/root/CLAUDE.md +62 -0
  69. package/stacks/cratis-csharp/templates/root/CratisApp.csproj +25 -0
  70. package/stacks/cratis-csharp/templates/root/CratisApp.sln +18 -0
  71. package/stacks/cratis-csharp/templates/root/GlobalUsings.cs +3 -0
  72. package/stacks/cratis-csharp/templates/root/Home.tsx +102 -0
  73. package/stacks/cratis-csharp/templates/root/Program.cs +26 -0
  74. package/stacks/cratis-csharp/templates/root/README.md +192 -0
  75. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/AllListings.ts +47 -0
  76. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/Listing.cs +11 -0
  77. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/Listing.ts +12 -0
  78. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/ListingDataTable.tsx +17 -0
  79. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/index.ts +1 -0
  80. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/Register.ts +51 -0
  81. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/RegisterDialog.tsx +18 -0
  82. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/Registration.cs +27 -0
  83. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/index.ts +1 -0
  84. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/SomeFeature.tsx +22 -0
  85. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/SomeName.cs +3 -0
  86. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/index.ts +1 -0
  87. package/stacks/cratis-csharp/templates/root/appsettings.Development.json +9 -0
  88. package/stacks/cratis-csharp/templates/root/appsettings.json +26 -0
  89. package/stacks/cratis-csharp/templates/root/docker-compose.yml +23 -0
  90. package/stacks/cratis-csharp/templates/root/package.json +33 -0
  91. package/stacks/cratis-csharp/templates/root/tsconfig.json +3 -0
  92. package/stacks/modeling-kit/templates/.claude/settings.local.json +13 -0
  93. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +215 -0
  94. package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +176 -0
  95. package/stacks/modeling-kit/templates/.claude/skills/connect/SKILL.md +182 -0
  96. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +383 -0
  97. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +465 -0
  98. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +587 -0
  99. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/facilitating-event-modeling-workshops.md +626 -0
  100. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +542 -0
  101. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +497 -0
  102. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +783 -0
  103. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +604 -0
  104. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +699 -0
  105. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +674 -0
  106. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +231 -0
  107. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +150 -0
  108. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +329 -0
  109. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +204 -0
  110. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +352 -0
  111. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/project-planning-with-event-modeling.md +341 -0
  112. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +153 -0
  113. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +136 -0
  114. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +78 -0
  115. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +85 -0
  116. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +655 -0
  117. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +479 -0
  118. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +234 -0
  119. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +353 -0
  120. package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +200 -0
  121. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +94 -0
  122. package/stacks/modeling-kit/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +441 -0
  123. package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +290 -0
  124. package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +212 -0
  125. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +123 -0
  126. package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +353 -0
  127. package/stacks/modeling-kit/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
  128. package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +134 -0
  129. package/stacks/modeling-kit/templates/kit/lib/agent.sh +20 -0
  130. package/stacks/modeling-kit/templates/kit/lib/ollama-agent.js +147 -0
  131. package/stacks/modeling-kit/templates/kit/lib/ralph.js +286 -0
  132. package/stacks/modeling-kit/templates/kit/package.json +12 -0
  133. package/stacks/modeling-kit/templates/kit/ralph-claude.js +50 -0
  134. package/stacks/modeling-kit/templates/kit/ralph-ollama.js +38 -0
  135. package/stacks/modeling-kit/templates/kit/ralph.sh +61 -0
  136. package/stacks/modeling-kit/templates/kit/realtime-agent.js +18 -0
  137. package/stacks/modeling-kit/templates/root/CLAUDE.md +56 -0
  138. package/stacks/node/templates/.claude/skills/build-automation/SKILL.md +266 -0
  139. package/stacks/node/templates/.claude/skills/build-state-change/SKILL.md +344 -0
  140. package/stacks/node/templates/.claude/skills/build-state-view/SKILL.md +412 -0
  141. package/stacks/node/templates/.claude/skills/connect/SKILL.md +178 -0
  142. package/stacks/node/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +628 -0
  143. package/stacks/node/templates/.claude/skills/load-slice/SKILL.md +143 -0
  144. package/stacks/node/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
  145. package/stacks/node/templates/build-kit/README.md +86 -0
  146. package/stacks/node/templates/build-kit/lib/AGENT.md +47 -0
  147. package/stacks/node/templates/build-kit/lib/agent.sh +20 -0
  148. package/stacks/node/templates/build-kit/lib/backend-prompt.md +152 -0
  149. package/stacks/node/templates/build-kit/lib/prompt.md +122 -0
  150. package/stacks/node/templates/build-kit/lib/ralph.js +369 -0
  151. package/stacks/node/templates/build-kit/ralph-claude.js +44 -0
  152. package/stacks/node/templates/build-kit/ralph.sh +98 -0
  153. package/stacks/node/templates/root/.env.example +15 -0
  154. package/stacks/node/templates/root/CLAUDE.md +60 -0
  155. package/stacks/node/templates/root/docker-compose.yml +15 -0
  156. package/stacks/node/templates/root/flyway.conf +17 -0
  157. package/stacks/node/templates/root/migrations/V1__schema.sql.example +12 -0
  158. package/stacks/node/templates/root/package.json +50 -0
  159. package/stacks/node/templates/root/server.ts +130 -0
  160. package/stacks/node/templates/root/setup-env.sh +53 -0
  161. package/stacks/node/templates/root/src/common/assertions.ts +6 -0
  162. package/stacks/node/templates/root/src/common/db.ts +32 -0
  163. package/stacks/node/templates/root/src/common/loadPostgresEventstore.ts +23 -0
  164. package/stacks/node/templates/root/src/common/parseEndpoint.ts +51 -0
  165. package/stacks/node/templates/root/src/common/processorDlq.ts +28 -0
  166. package/stacks/node/templates/root/src/common/realtimeBroadcast.ts +12 -0
  167. package/stacks/node/templates/root/src/common/replay.ts +16 -0
  168. package/stacks/node/templates/root/src/common/routes.ts +19 -0
  169. package/stacks/node/templates/root/src/common/testHelpers.ts +44 -0
  170. package/stacks/node/templates/root/src/swagger.ts +34 -0
  171. package/stacks/node/templates/root/src/util/assertions.ts +6 -0
  172. package/stacks/node/templates/root/src/util/hash.ts +9 -0
  173. package/stacks/node/templates/root/src/util/sanitize.ts +23 -0
  174. package/stacks/node/templates/root/tsconfig.json +32 -0
  175. package/stacks/node/templates/root/vercel.json +8 -0
  176. package/stacks/supabase/templates/.claude/skills/build-automation/SKILL.md +266 -0
  177. package/stacks/supabase/templates/.claude/skills/build-state-change/SKILL.md +344 -0
  178. package/stacks/supabase/templates/.claude/skills/build-state-view/SKILL.md +390 -0
  179. package/stacks/supabase/templates/.claude/skills/build-webhook/SKILL.md +303 -0
  180. package/stacks/supabase/templates/.claude/skills/connect/SKILL.md +178 -0
  181. package/stacks/supabase/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +628 -0
  182. package/stacks/supabase/templates/.claude/skills/load-slice/SKILL.md +143 -0
  183. package/stacks/supabase/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
  184. package/stacks/supabase/templates/build-kit/README.md +86 -0
  185. package/stacks/supabase/templates/build-kit/lib/AGENT.md +47 -0
  186. package/stacks/supabase/templates/build-kit/lib/agent.sh +20 -0
  187. package/stacks/supabase/templates/build-kit/lib/backend-prompt.md +152 -0
  188. package/stacks/supabase/templates/build-kit/lib/prompt.md +123 -0
  189. package/stacks/supabase/templates/build-kit/lib/ralph.js +369 -0
  190. package/stacks/supabase/templates/build-kit/ralph-claude.js +44 -0
  191. package/stacks/supabase/templates/build-kit/ralph.sh +98 -0
  192. package/stacks/supabase/templates/root/.env.example +22 -0
  193. package/stacks/supabase/templates/root/CLAUDE.md +61 -0
  194. package/stacks/supabase/templates/root/flyway.conf +17 -0
  195. package/stacks/supabase/templates/root/package.json +52 -0
  196. package/stacks/supabase/templates/root/server.ts +177 -0
  197. package/stacks/supabase/templates/root/setup-env.sh +53 -0
  198. package/stacks/supabase/templates/root/src/common/assertions.ts +6 -0
  199. package/stacks/supabase/templates/root/src/common/db.ts +32 -0
  200. package/stacks/supabase/templates/root/src/common/loadPostgresEventstore.ts +23 -0
  201. package/stacks/supabase/templates/root/src/common/parseEndpoint.ts +51 -0
  202. package/stacks/supabase/templates/root/src/common/processorDlq.ts +28 -0
  203. package/stacks/supabase/templates/root/src/common/realtimeBroadcast.ts +19 -0
  204. package/stacks/supabase/templates/root/src/common/replay.ts +16 -0
  205. package/stacks/supabase/templates/root/src/common/routes.ts +23 -0
  206. package/stacks/supabase/templates/root/src/common/testHelpers.ts +54 -0
  207. package/stacks/supabase/templates/root/src/supabase/README.md +141 -0
  208. package/stacks/supabase/templates/root/src/supabase/api.ts +56 -0
  209. package/stacks/supabase/templates/root/src/supabase/requireOrgaAdmin.ts +32 -0
  210. package/stacks/supabase/templates/root/src/supabase/requireSysUser.ts +40 -0
  211. package/stacks/supabase/templates/root/src/supabase/requireUser.ts +72 -0
  212. package/stacks/supabase/templates/root/src/swagger.ts +34 -0
  213. package/stacks/supabase/templates/root/src/util/assertions.ts +6 -0
  214. package/stacks/supabase/templates/root/src/util/hash.ts +9 -0
  215. package/stacks/supabase/templates/root/src/util/sanitize.ts +23 -0
  216. package/stacks/supabase/templates/root/supabase/config.toml +295 -0
  217. package/stacks/supabase/templates/root/supabase/migrations/V1__schema.sql.example +12 -0
  218. package/stacks/supabase/templates/root/supabase/seed.sql +1 -0
  219. package/stacks/supabase/templates/root/tsconfig.json +32 -0
  220. package/stacks/supabase/templates/root/vercel.json +8 -0
@@ -0,0 +1,231 @@
1
+ ---
2
+ name: eventmodeling-optimizing-stream-design
3
+ description: "Design event streams with proper stream identity to keep streams appropriately sized, avoid unnecessary snapshotting, and balance performance with simplicity. Use when concerned about stream length, planning performance, or validating stream design before implementation. Do not use for: designing the initial event model structure (use eventmodeling-designing-event-models) or general architectural validation (use eventmodeling-validating-event-models)."
4
+ allowed-tools:
5
+ - AskUserQuestion
6
+ - Write
7
+ ---
8
+
9
+ # Optimizing Stream Design
10
+
11
+ ## Interview Phase (Optional)
12
+
13
+ **When to Interview**: Skip if the user has specified: expected event frequency, stream lifetime, and growth patterns. Interview when stream length concerns exist but growth estimates are unclear.
14
+
15
+ **Interview Strategy**: Establish growth expectations and performance requirements before recommending snapshotting. Most snapshotting proposals stem from poor stream boundary design, not genuine volume — surface the estimates first to distinguish real performance concerns from design problems.
16
+
17
+ ### Critical Questions
18
+
19
+ 1. **Growth Estimates** (Impact: Determines if snapshotting is needed or if stream design should change)
20
+ - Question: "Estimate events: (A) Per entity per day, (B) Lifetime total, (C) Growth over years. Example: 5-10 events/order, 1-10 million orders/year?"
21
+ - Why it matters: Growth estimates reveal if streams will genuinely be too long or if design is wrong
22
+ - Follow-up triggers: If estimates exceed 300 events per stream lifetime → ask "Is the stream identity correct? Could this stream be split by a narrower business entity?"
23
+
24
+ 2. **Performance SLAs** (Impact: Determines acceptable latency and snapshotting decisions)
25
+ - Question: "Performance requirements? (A) <100ms read latency, (B) <1s acceptable, (C) Eventual consistency OK?"
26
+ - Why it matters: Strict SLAs might need snapshotting; loose SLAs often don't
27
+ - Follow-up triggers: If (A) → ask "Which commands specifically need sub-100ms replay? Are those commands reading from a read model or replaying the stream directly?"
28
+
29
+ ### Interview Flow
30
+
31
+ **Conditional Entry**:
32
+ ```
33
+ If user has provided:
34
+ - Event frequency estimate (per entity per day or per transaction)
35
+ - AND stream lifetime estimate (months or years)
36
+ - AND read latency SLA (or confirmation that eventual consistency is acceptable)
37
+
38
+ Then: Skip interview, proceed directly to stream analysis
39
+
40
+ Else: Conduct interview
41
+ ```
42
+
43
+ **Phase 1: Growth Estimation** (Question 1)
44
+ - Establish per-entity event volume
45
+ - Project lifetime stream length using the estimation formula
46
+ - Determine whether redesign or snapshotting analysis is warranted
47
+
48
+ **Phase 2: SLA Requirements** (Question 2)
49
+ - Identify read latency requirements per command
50
+ - Determine whether read models or direct stream replay satisfies the SLA
51
+ - Establish whether snapshotting is justified by SLA alone
52
+
53
+ ### Capturing Interview Findings
54
+
55
+ Append findings to the project's event modeling file:
56
+
57
+ **File**: `.trogonai/interviews/[project-name]/EVENTMODELING.md`
58
+
59
+ Use Write tool to add/update this section:
60
+
61
+ ```markdown
62
+ ## Optimizing Stream Design (eventmodeling-optimizing-stream-design)
63
+
64
+ ### Growth Estimates
65
+ [From Q1: Events per entity per day, lifetime total, annual growth]
66
+
67
+ ### Performance SLAs
68
+ [From Q2: Latency requirements per command or view]
69
+
70
+ ### Optimization Decisions
71
+ - Streams requiring redesign: [list or "None"]
72
+ - Streams where snapshotting is justified: [list or "None"]
73
+ - Streams within acceptable bounds: [list]
74
+ ```
75
+
76
+ Update Interview Trail:
77
+ ```markdown
78
+ | Optimization | eventmodeling-optimizing-stream-design | Done | Stream growth estimates, SLA review, snapshotting decisions |
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Stream Design Optimization
84
+
85
+ **Purpose**: Optimize event stream design by validating stream boundaries, estimating growth, and making snapshotting decisions based on design quality—not just size.
86
+
87
+ **Applies To**: Any domain - e-commerce, banking, SaaS, marketplace, healthcare, etc.
88
+
89
+ **When to Use**:
90
+ - After defining event streams in domain analysis
91
+ - When concerned about stream length or performance
92
+ - Before implementing to validate stream design
93
+ - During performance planning to determine snapshotting strategy
94
+ - When redesigning streams for scalability
95
+
96
+ **What It Does**:
97
+ 1. Analyzes event stream design for proper event organization
98
+ 2. Estimates stream growth over time
99
+ 3. Identifies when snapshotting is genuinely needed vs. design issue
100
+ 4. Recommends optimal stream identity boundaries
101
+ 5. Balances performance optimization against complexity
102
+ 6. Provides snapshotting strategy without over-engineering
103
+
104
+ ---
105
+
106
+ ## Core Principle: Design First, Snapshot Second
107
+
108
+ **Golden Rule**:
109
+ > If you find yourself needing to snapshot because the stream is too long, first ask: "Is my stream identity wrong?" Usually, the answer is yes.
110
+
111
+ Snapshotting is a **performance optimization**, not a design problem. Good stream design (proper identity boundaries) often eliminates the need for snapshotting entirely.
112
+
113
+ ---
114
+
115
+ ## Stream Design Analysis Framework
116
+
117
+ ### 1. Estimate Stream Growth
118
+
119
+ **Formula**:
120
+ ```
121
+ Estimated Stream Length (total events/instance) =
122
+ Events Per Aggregate Instance Per Year (events/instance/year)
123
+ × Lifetime of Instance (years)
124
+ × Annual Growth Factor (dimensionless year-over-year multiplier ≥ 1.0)
125
+ ```
126
+
127
+ **Quick Examples**:
128
+
129
+ **E-commerce Order**: 8 events/year × 1.5 year lifetime = 8-16 events → NOT NEEDED
130
+
131
+ **Banking Account**: 100-200 events/year × 10 years = 1000-2000 events → CONSIDER AT 1000+
132
+
133
+ **Order Processing**: 100+ events/year × 5 years = 300-500+ events → PROBABLY NEEDED
134
+
135
+ **SaaS User**: 12-60 events/year × 5 years = 60-300 events → RARELY NEEDED
136
+
137
+ ### 2. Identify Stream Length Categories
138
+
139
+ | Length | Status | Action | Snapshotting |
140
+ |--------|--------|--------|--------------|
141
+ | < 50 events | IDEAL | Keep as-is | NOT NEEDED |
142
+ | 50-100 events | GOOD | Monitor growth | NOT NEEDED |
143
+ | 100-300 events | ACCEPTABLE | Review boundary | CONSIDER if replayed |
144
+ | 300-1000 events | LONG | REDESIGN first | Only last resort |
145
+ | 1000+ events | CRITICAL | REDESIGN required | Won't help |
146
+
147
+ ---
148
+
149
+ ## Quick Decision Matrix
150
+
151
+ | Stream Length | Read Pattern | Frequency | Action |
152
+ |---|---|---|---|
153
+ | < 50 | Any | Any | IDEAL - Keep as-is |
154
+ | 50-100 | Any | Any | Good - Monitor |
155
+ | 100-300 | From Model | Any | OK - No snapshot |
156
+ | 100-300 | Stream Replay | Low | OK - Monitor |
157
+ | 100-300 | Stream Replay | High | REDESIGN |
158
+ | 300-1000 | From Model | Any | OK - No snapshot |
159
+ | 300-1000 | Stream Replay | Any | REDESIGN |
160
+ | 1000+ | Any | Any | CRITICAL - REDESIGN |
161
+
162
+ ---
163
+
164
+ ## Reference Files
165
+
166
+ **Aggregate Boundary Design**: See [patterns.md](references/patterns.md) for:
167
+ - 5 aggregate patterns (single entity, composite, collections, event logs, historical)
168
+ - Stream size decision tree
169
+ - Red flags that indicate redesign needed
170
+ - Tips for optimal stream design
171
+
172
+ **Snapshotting Strategy**: See [snapshotting.md](references/snapshotting.md) for:
173
+ - Criteria for when snapshotting is truly needed
174
+ - Context-based decision thresholds
175
+ - Snapshot frequency, versioning, and cleanup strategies
176
+ - Cost-benefit analysis
177
+
178
+ **Domain-Specific Guidance**: See [domain-patterns.md](references/domain-patterns.md) for:
179
+ - E-commerce patterns (orders, carts, accounts)
180
+ - Banking patterns (accounts, transactions, loans)
181
+ - SaaS patterns (subscriptions, workspaces, data collections)
182
+ - Implementation checklist
183
+
184
+ ---
185
+
186
+ ## Key Insights
187
+
188
+ ### Why Snapshotting Usually Isn't the Answer
189
+
190
+ ```
191
+ Before implementing snapshotting, ask:
192
+
193
+ 1. Can I split this aggregate into smaller ones?
194
+ → YES: Do that instead. Simpler, better design.
195
+
196
+ 2. Can I reduce event granularity?
197
+ → YES: Batch events or create coarser state changes.
198
+
199
+ 3. Am I using a read model for this aggregate?
200
+ → NO: Create a read model (cached projection).
201
+ Stream size becomes irrelevant.
202
+
203
+ 4. Have I measured actual replay latency?
204
+ → NO: Measure first. Most systems exceed expectations.
205
+
206
+ If ANY of these is YES, do that before snapshotting.
207
+ Only after exhausting design improvements, consider snapshots.
208
+ ```
209
+
210
+ ### The Snapshotting Trade-off
211
+
212
+ ```
213
+ Snapshotting Complexity ≈ 2-3x Complexity of Better Design
214
+
215
+ Before snapshot: 50 lines of code, simple, testable
216
+ With snapshots: 150+ lines, versioning, recovery logic, testing matrix
217
+
218
+ Better to redesign and keep streams < 300 events.
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Quality Checklist
224
+
225
+ - [ ] Each stream is identified by a business entity identity (e.g., `orderId`), not a category or type
226
+ - [ ] No stream grows unboundedly without a design reason — event frequency and stream lifetime estimated
227
+ - [ ] Streams under 1000 events require no snapshotting justification
228
+ - [ ] If snapshotting is proposed, all simpler alternatives (split stream, shorter lifetime) have been eliminated first
229
+ - [ ] Command handler state is reconstructed from stream events — no persistent state stored outside the stream
230
+ - [ ] Each stream can be independently versioned and replayed without affecting other streams
231
+
@@ -0,0 +1,150 @@
1
+ # Domain-Specific Stream Size Patterns
2
+
3
+ ## Contents
4
+ - E-commerce patterns
5
+ - Banking patterns
6
+ - SaaS patterns
7
+ - Implementation checklist
8
+
9
+ ---
10
+
11
+ ## E-commerce Domain
12
+
13
+ **Order Aggregate**:
14
+ ```
15
+ Events: 5-20
16
+ 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
21
+ ```
22
+
23
+ **Shopping Cart Aggregate**:
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
31
+ ```
32
+
33
+ **User Account Aggregate**:
34
+ ```
35
+ Events: 2-10 per year (profile updates, settings changes)
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
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Banking Domain
46
+
47
+ **Account Aggregate**:
48
+ ```
49
+ Events: 50-500+ per year (deposits, withdrawals, fees)
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
56
+ ```
57
+
58
+ **Transaction Aggregate**:
59
+ ```
60
+ Events: 1-5 (Requested → Processing → Settled)
61
+ 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
66
+ ```
67
+
68
+ **Loan Aggregate**:
69
+ ```
70
+ Events: 100-500+ (payments, rate changes, modifications)
71
+ 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
77
+ ```
78
+
79
+ ---
80
+
81
+ ## SaaS Domain
82
+
83
+ **Subscription Aggregate**:
84
+ ```
85
+ Events: 2-20 (Created, Upgraded, Downgraded, Cancelled)
86
+ 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
91
+ ```
92
+
93
+ **User Workspace Aggregate**:
94
+ ```
95
+ Events: 10-100+ (members added, roles changed, settings updated)
96
+ 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**:
104
+ ```
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?
146
+ ```
147
+
148
+ **If ANY question suggests redesign is better**: Redesign first, snapshot never.
149
+
150
+ **If ALL questions support snapshotting**: Proceed with implementation.
@@ -0,0 +1,329 @@
1
+ # Aggregate Boundary Design Patterns
2
+
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
8
+
9
+ ---
10
+
11
+ ## Aggregate Boundary Design Patterns
12
+
13
+ ### Pattern 1: Single Entity (Most Common)
14
+
15
+ CORRECT: One aggregate per entity
16
+ ```
17
+ Aggregate: Order
18
+ Root Identity: orderId (e.g., 'order-123')
19
+ Entity: The specific order
20
+ Lifetime: 1-2 years
21
+
22
+ Events in stream:
23
+ 1. OrderCreated (2024-01-15)
24
+ 2. OrderLineAdded (2024-01-15)
25
+ 3. OrderLineAdded (2024-01-15)
26
+ 4. OrderConfirmed (2024-01-16)
27
+ 5. PaymentProcessed (2024-01-16)
28
+ 6. OrderShipped (2024-01-20)
29
+ 7. OrderDelivered (2024-01-25)
30
+
31
+ Stream Length: 7 events
32
+ Snapshotting: NOT NEEDED
33
+
34
+ Identity Principle: orderId is the natural business key
35
+ Boundary: Everything about THIS specific order, nothing else
36
+ Consistency: Only one order being modified at a time
37
+ ```
38
+
39
+ ---
40
+
41
+ ### Pattern 2: Composite Entity (Proper Composition)
42
+
43
+ CORRECT: Aggregate contains related child entities
44
+ ```
45
+ Aggregate: Order
46
+ Root Identity: orderId (e.g., 'order-456')
47
+
48
+ Contains related children (same lifetime):
49
+ - OrderLines: 3 items
50
+ * Line 1: productId=prod-A, qty=2, price=$50
51
+ * Line 2: productId=prod-B, qty=1, price=$100
52
+ * Line 3: productId=prod-C, qty=5, price=$10
53
+
54
+ - ShippingAddress:
55
+ street: 123 Main St, City: Portland, State: OR
56
+
57
+ - PaymentInfo:
58
+ method: credit_card, amount: $400
59
+
60
+ Events in stream:
61
+ 1. OrderCreated (customer-789, 3 items)
62
+ 2. OrderLineAdded (item 1)
63
+ 3. OrderLineAdded (item 2)
64
+ 4. OrderLineAdded (item 3)
65
+ 5. OrderConfirmed (payment method selected)
66
+ 6. PaymentProcessed (authorization complete)
67
+ 7. OrderShipped (tracking 123456)
68
+
69
+ Stream Length: 7 events
70
+ Snapshotting: NOT NEEDED (well under 1000)
71
+
72
+ Pattern: Small, bounded number of children per parent
73
+ Lifetime: Parent and all children created/destroyed together
74
+ Consistency: All modified as a unit (can't ship without payment, etc.)
75
+ ```
76
+
77
+ ---
78
+
79
+ ### Pattern 3: Collection (ANTI-PATTERN - DO NOT USE)
80
+
81
+ WRONG: Treating a collection as aggregate
82
+ ```
83
+ Bad Aggregate: AllOrders
84
+ Root Identity: "all-orders-collection" (artificial, meaningless)
85
+
86
+ Contains: Every order ever created
87
+ Events:
88
+ 1. OrderCreated (customer-001, order-001)
89
+ 2. OrderCreated (customer-002, order-002)
90
+ 3. OrderCreated (customer-001, order-003)
91
+ 4. OrderCreated (customer-003, order-004)
92
+ ... (continues forever, unbounded)
93
+
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
+ Problems with this approach:
102
+ - 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)
107
+
108
+ Solution: Use a projection/read model query instead, not an aggregate
109
+ - Query: "GetAllOrdersByCustomer(customer-id)"
110
+ - Query: "GetOrdersByStatus(status)"
111
+ - Rebuild from individual Order streams on-demand
112
+ ```
113
+
114
+ ---
115
+
116
+ ### Pattern 4: Event Log (ANTI-PATTERN - DO NOT USE)
117
+
118
+ WRONG: Using aggregate as event log
119
+ ```
120
+ Bad Aggregate: SystemLog
121
+ Root Identity: "system-log" (meaningless placeholder)
122
+
123
+ Contains: Every system event imaginable
124
+ Events:
125
+ 1. UserLoggedIn (user-123)
126
+ 2. OrderCreated (order-456)
127
+ 3. PaymentProcessed (payment-789)
128
+ 4. InventoryUpdated (sku-101)
129
+ 5. UserLoggedOut (user-123)
130
+ 6. UserLoggedIn (user-223)
131
+ ... (grows indefinitely, no pattern)
132
+
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
+ Problems with this approach:
140
+ - No business identity (log of everything)
141
+ - Events unrelated to each other (mixing user, order, payment, inventory)
142
+ - No consistency boundary (user login != order creation)
143
+ - 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
+
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
151
+ ```
152
+
153
+ ---
154
+
155
+ ### Pattern 5: Historical Aggregate (GOOD - When Needed)
156
+
157
+ CORRECT: Keep historical data for audit/compliance
158
+ ```
159
+ Aggregate: ArchivedOrder
160
+ Root Identity: archivedOrderId (e.g., 'archived-order-001')
161
+ Purpose: Regulatory compliance (7-year retention)
162
+
163
+ Contains: Snapshot + audit trail of an order
164
+ Events:
165
+ 1. OrderArchived (original order-123 on 2023-12-31)
166
+ - reason: compliance_retention
167
+ - originalData: { id, customerId, items, total, dates }
168
+
169
+ 2. AuditLogAdded (accessed by accounting, 2024-01-15)
170
+ - accessor: accounting@company.com
171
+ - action: viewed for tax audit
172
+
173
+ 3. AuditLogAdded (accessed by auditor, 2024-02-01)
174
+ - accessor: auditor@firm.com
175
+ - action: reviewed for compliance
176
+
177
+ ... (additional audit entries over time)
178
+
179
+ Lifetime: 7 years (regulatory requirement)
180
+ Stream Length: 500-2000 events (audit entries added slowly)
181
+ Snapshotting: Not needed (historical, not active)
182
+
183
+ Key architectural principles:
184
+ - Completely separate from active Order aggregate
185
+ - Active Order is for current business operations
186
+ - Archived Order is immutable historical record
187
+ - Different access patterns, different SLAs
188
+ ```
189
+
190
+ ---
191
+
192
+ ## Stream Size Decision Tree
193
+
194
+ Use this to decide if your streams are properly designed:
195
+
196
+ ```
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
200
+
201
+ YES → How many events does it accumulate?
202
+
203
+ < 100 events
204
+ PERFECT: No optimization needed
205
+
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
218
+ 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
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Red Flags: Redesign Needed (Not Snapshotting)
249
+
250
+ If your stream exhibits ANY of these, snapshotting won't help—you need to redesign:
251
+
252
+ ```
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
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Tips for Optimal Stream Design
287
+
288
+ ### 1. Favor Redesign Over Snapshotting
289
+ ```
290
+ Cost: Redesign effort < Snapshotting maintenance
291
+ Quality: Better design > Better optimization
292
+ Future: Smaller streams are easier to scale
293
+ ```
294
+
295
+ ### 2. Understand Event Granularity
296
+ ```
297
+ RIGHT: One event per meaningful state change
298
+ WRONG: Multiple events per semantic operation
299
+ Example: "UserUpdatedProfile" (1 event)
300
+ NOT: "FirstNameChanged", "LastNameChanged", ... (N events)
301
+ ```
302
+
303
+ ### 3. Split When Possible
304
+ ```
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)
313
+ ```
314
+
315
+ ### 4. Archive Old Data
316
+ ```
317
+ Keep everything in active aggregate
318
+ Move completed/closed data to archive aggregate
319
+ Example:
320
+ - ActiveSubscription (current state)
321
+ - ArchivedSubscription (after cancelled)
322
+ ```
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
+ ```