@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.
- package/README.md +175 -0
- package/cli.js +676 -0
- package/package.json +35 -0
- package/shared/build-kit/code-export.mjs +560 -0
- package/shared/build-kit/lib/ollama-agent.js +147 -0
- package/shared/build-kit/package.json +11 -0
- package/shared/build-kit/ralph-ollama.js +39 -0
- package/shared/build-kit/realtime-agent.js +18 -0
- package/stacks/axon/templates/.claude/skills/build-automation/SKILL.md +407 -0
- package/stacks/axon/templates/.claude/skills/build-automation/references/feature-flag-patterns.md +114 -0
- package/stacks/axon/templates/.claude/skills/build-automation-workflow/SKILL.md +496 -0
- package/stacks/axon/templates/.claude/skills/build-automation-workflow/references/axon-workflow-api.md +279 -0
- package/stacks/axon/templates/.claude/skills/build-automation-workflow/references/feature-flag-patterns.md +114 -0
- package/stacks/axon/templates/.claude/skills/build-state-change/SKILL.md +396 -0
- package/stacks/axon/templates/.claude/skills/build-state-change/references/axon-test-fixture-patterns.md +162 -0
- package/stacks/axon/templates/.claude/skills/build-state-change/references/feature-flag-patterns.md +56 -0
- package/stacks/axon/templates/.claude/skills/build-state-change/references/rest-api-patterns.md +115 -0
- package/stacks/axon/templates/.claude/skills/build-state-view/SKILL.md +282 -0
- package/stacks/axon/templates/.claude/skills/connect/SKILL.md +178 -0
- package/stacks/axon/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +611 -0
- package/stacks/axon/templates/.claude/skills/load-slice/SKILL.md +141 -0
- package/stacks/axon/templates/.claude/skills/update-slice-status/SKILL.md +105 -0
- package/stacks/axon/templates/build-kit/lib/AGENT.md +47 -0
- package/stacks/axon/templates/build-kit/lib/agent.sh +20 -0
- package/stacks/axon/templates/build-kit/lib/backend-prompt.md +158 -0
- package/stacks/axon/templates/build-kit/lib/prompt.md +126 -0
- package/stacks/axon/templates/build-kit/lib/ralph.js +369 -0
- package/stacks/axon/templates/build-kit/ralph-claude.js +44 -0
- package/stacks/axon/templates/build-kit/ralph.sh +98 -0
- package/stacks/axon/templates/root/.env.example +5 -0
- package/stacks/axon/templates/root/CLAUDE.md +60 -0
- package/stacks/axon/templates/root/README.md +44 -0
- package/stacks/axon/templates/root/docker-compose.yml +64 -0
- package/stacks/axon/templates/root/mvnw +259 -0
- package/stacks/axon/templates/root/mvnw.cmd +149 -0
- package/stacks/axon/templates/root/pom.xml +125 -0
- package/stacks/axon/templates/root/src/main/java/io/axoniq/quickstart/QuickstartApplication.java +128 -0
- package/stacks/axon/templates/root/src/main/java/io/axoniq/quickstart/config/WebConfig.java +155 -0
- package/stacks/axon/templates/root/src/main/resources/application.properties +2 -0
- package/stacks/axon/templates/root/src/main/resources/static/bg-body.png +0 -0
- package/stacks/axon/templates/root/src/main/resources/static/bg-header.png +0 -0
- package/stacks/axon/templates/root/src/main/resources/static/index.html +596 -0
- package/stacks/cratis-csharp/templates/.claude/skills/_shared/cratis-conventions.md +251 -0
- package/stacks/cratis-csharp/templates/.claude/skills/build-automation/SKILL.md +99 -0
- package/stacks/cratis-csharp/templates/.claude/skills/build-automation/references/patterns.md +115 -0
- package/stacks/cratis-csharp/templates/.claude/skills/build-state-change/SKILL.md +163 -0
- package/stacks/cratis-csharp/templates/.claude/skills/build-state-change/references/patterns.md +234 -0
- package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/SKILL.md +120 -0
- package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/references/patterns.md +166 -0
- package/stacks/cratis-csharp/templates/.claude/skills/connect/SKILL.md +169 -0
- package/stacks/cratis-csharp/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +609 -0
- package/stacks/cratis-csharp/templates/.claude/skills/load-slice/SKILL.md +141 -0
- package/stacks/cratis-csharp/templates/.claude/skills/update-slice-status/SKILL.md +105 -0
- package/stacks/cratis-csharp/templates/build-kit/lib/AGENT.md +58 -0
- package/stacks/cratis-csharp/templates/build-kit/lib/agent.sh +20 -0
- package/stacks/cratis-csharp/templates/build-kit/lib/backend-prompt.md +129 -0
- package/stacks/cratis-csharp/templates/build-kit/lib/prompt.md +124 -0
- package/stacks/cratis-csharp/templates/build-kit/lib/ralph.js +302 -0
- package/stacks/cratis-csharp/templates/build-kit/ralph-claude.js +37 -0
- package/stacks/cratis-csharp/templates/build-kit/ralph.sh +98 -0
- package/stacks/cratis-csharp/templates/root/.frontend/index.css +29 -0
- package/stacks/cratis-csharp/templates/root/.frontend/index.html +17 -0
- package/stacks/cratis-csharp/templates/root/.frontend/main.tsx +18 -0
- package/stacks/cratis-csharp/templates/root/.frontend/tsconfig.json +42 -0
- package/stacks/cratis-csharp/templates/root/.frontend/tsconfig.node.json +11 -0
- package/stacks/cratis-csharp/templates/root/.frontend/vite.config.ts +56 -0
- package/stacks/cratis-csharp/templates/root/App.tsx +23 -0
- package/stacks/cratis-csharp/templates/root/CLAUDE.md +62 -0
- package/stacks/cratis-csharp/templates/root/CratisApp.csproj +25 -0
- package/stacks/cratis-csharp/templates/root/CratisApp.sln +18 -0
- package/stacks/cratis-csharp/templates/root/GlobalUsings.cs +3 -0
- package/stacks/cratis-csharp/templates/root/Home.tsx +102 -0
- package/stacks/cratis-csharp/templates/root/Program.cs +26 -0
- package/stacks/cratis-csharp/templates/root/README.md +192 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/AllListings.ts +47 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/Listing.cs +11 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/Listing.ts +12 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/ListingDataTable.tsx +17 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/index.ts +1 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/Register.ts +51 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/RegisterDialog.tsx +18 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/Registration.cs +27 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/index.ts +1 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/SomeFeature.tsx +22 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/SomeName.cs +3 -0
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/index.ts +1 -0
- package/stacks/cratis-csharp/templates/root/appsettings.Development.json +9 -0
- package/stacks/cratis-csharp/templates/root/appsettings.json +26 -0
- package/stacks/cratis-csharp/templates/root/docker-compose.yml +23 -0
- package/stacks/cratis-csharp/templates/root/package.json +33 -0
- package/stacks/cratis-csharp/templates/root/tsconfig.json +3 -0
- package/stacks/modeling-kit/templates/.claude/settings.local.json +13 -0
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +215 -0
- package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +176 -0
- package/stacks/modeling-kit/templates/.claude/skills/connect/SKILL.md +182 -0
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +383 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +465 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +587 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/facilitating-event-modeling-workshops.md +626 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +542 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +497 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +783 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +604 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +699 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +674 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +231 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +150 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +329 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +204 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +352 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/project-planning-with-event-modeling.md +341 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +153 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +136 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +78 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +85 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +655 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +479 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +234 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +353 -0
- package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +200 -0
- package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +94 -0
- package/stacks/modeling-kit/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +441 -0
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +290 -0
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +212 -0
- package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +123 -0
- package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +353 -0
- package/stacks/modeling-kit/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
- package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +134 -0
- package/stacks/modeling-kit/templates/kit/lib/agent.sh +20 -0
- package/stacks/modeling-kit/templates/kit/lib/ollama-agent.js +147 -0
- package/stacks/modeling-kit/templates/kit/lib/ralph.js +286 -0
- package/stacks/modeling-kit/templates/kit/package.json +12 -0
- package/stacks/modeling-kit/templates/kit/ralph-claude.js +50 -0
- package/stacks/modeling-kit/templates/kit/ralph-ollama.js +38 -0
- package/stacks/modeling-kit/templates/kit/ralph.sh +61 -0
- package/stacks/modeling-kit/templates/kit/realtime-agent.js +18 -0
- package/stacks/modeling-kit/templates/root/CLAUDE.md +56 -0
- package/stacks/node/templates/.claude/skills/build-automation/SKILL.md +266 -0
- package/stacks/node/templates/.claude/skills/build-state-change/SKILL.md +344 -0
- package/stacks/node/templates/.claude/skills/build-state-view/SKILL.md +412 -0
- package/stacks/node/templates/.claude/skills/connect/SKILL.md +178 -0
- package/stacks/node/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +628 -0
- package/stacks/node/templates/.claude/skills/load-slice/SKILL.md +143 -0
- package/stacks/node/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
- package/stacks/node/templates/build-kit/README.md +86 -0
- package/stacks/node/templates/build-kit/lib/AGENT.md +47 -0
- package/stacks/node/templates/build-kit/lib/agent.sh +20 -0
- package/stacks/node/templates/build-kit/lib/backend-prompt.md +152 -0
- package/stacks/node/templates/build-kit/lib/prompt.md +122 -0
- package/stacks/node/templates/build-kit/lib/ralph.js +369 -0
- package/stacks/node/templates/build-kit/ralph-claude.js +44 -0
- package/stacks/node/templates/build-kit/ralph.sh +98 -0
- package/stacks/node/templates/root/.env.example +15 -0
- package/stacks/node/templates/root/CLAUDE.md +60 -0
- package/stacks/node/templates/root/docker-compose.yml +15 -0
- package/stacks/node/templates/root/flyway.conf +17 -0
- package/stacks/node/templates/root/migrations/V1__schema.sql.example +12 -0
- package/stacks/node/templates/root/package.json +50 -0
- package/stacks/node/templates/root/server.ts +130 -0
- package/stacks/node/templates/root/setup-env.sh +53 -0
- package/stacks/node/templates/root/src/common/assertions.ts +6 -0
- package/stacks/node/templates/root/src/common/db.ts +32 -0
- package/stacks/node/templates/root/src/common/loadPostgresEventstore.ts +23 -0
- package/stacks/node/templates/root/src/common/parseEndpoint.ts +51 -0
- package/stacks/node/templates/root/src/common/processorDlq.ts +28 -0
- package/stacks/node/templates/root/src/common/realtimeBroadcast.ts +12 -0
- package/stacks/node/templates/root/src/common/replay.ts +16 -0
- package/stacks/node/templates/root/src/common/routes.ts +19 -0
- package/stacks/node/templates/root/src/common/testHelpers.ts +44 -0
- package/stacks/node/templates/root/src/swagger.ts +34 -0
- package/stacks/node/templates/root/src/util/assertions.ts +6 -0
- package/stacks/node/templates/root/src/util/hash.ts +9 -0
- package/stacks/node/templates/root/src/util/sanitize.ts +23 -0
- package/stacks/node/templates/root/tsconfig.json +32 -0
- package/stacks/node/templates/root/vercel.json +8 -0
- package/stacks/supabase/templates/.claude/skills/build-automation/SKILL.md +266 -0
- package/stacks/supabase/templates/.claude/skills/build-state-change/SKILL.md +344 -0
- package/stacks/supabase/templates/.claude/skills/build-state-view/SKILL.md +390 -0
- package/stacks/supabase/templates/.claude/skills/build-webhook/SKILL.md +303 -0
- package/stacks/supabase/templates/.claude/skills/connect/SKILL.md +178 -0
- package/stacks/supabase/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +628 -0
- package/stacks/supabase/templates/.claude/skills/load-slice/SKILL.md +143 -0
- package/stacks/supabase/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
- package/stacks/supabase/templates/build-kit/README.md +86 -0
- package/stacks/supabase/templates/build-kit/lib/AGENT.md +47 -0
- package/stacks/supabase/templates/build-kit/lib/agent.sh +20 -0
- package/stacks/supabase/templates/build-kit/lib/backend-prompt.md +152 -0
- package/stacks/supabase/templates/build-kit/lib/prompt.md +123 -0
- package/stacks/supabase/templates/build-kit/lib/ralph.js +369 -0
- package/stacks/supabase/templates/build-kit/ralph-claude.js +44 -0
- package/stacks/supabase/templates/build-kit/ralph.sh +98 -0
- package/stacks/supabase/templates/root/.env.example +22 -0
- package/stacks/supabase/templates/root/CLAUDE.md +61 -0
- package/stacks/supabase/templates/root/flyway.conf +17 -0
- package/stacks/supabase/templates/root/package.json +52 -0
- package/stacks/supabase/templates/root/server.ts +177 -0
- package/stacks/supabase/templates/root/setup-env.sh +53 -0
- package/stacks/supabase/templates/root/src/common/assertions.ts +6 -0
- package/stacks/supabase/templates/root/src/common/db.ts +32 -0
- package/stacks/supabase/templates/root/src/common/loadPostgresEventstore.ts +23 -0
- package/stacks/supabase/templates/root/src/common/parseEndpoint.ts +51 -0
- package/stacks/supabase/templates/root/src/common/processorDlq.ts +28 -0
- package/stacks/supabase/templates/root/src/common/realtimeBroadcast.ts +19 -0
- package/stacks/supabase/templates/root/src/common/replay.ts +16 -0
- package/stacks/supabase/templates/root/src/common/routes.ts +23 -0
- package/stacks/supabase/templates/root/src/common/testHelpers.ts +54 -0
- package/stacks/supabase/templates/root/src/supabase/README.md +141 -0
- package/stacks/supabase/templates/root/src/supabase/api.ts +56 -0
- package/stacks/supabase/templates/root/src/supabase/requireOrgaAdmin.ts +32 -0
- package/stacks/supabase/templates/root/src/supabase/requireSysUser.ts +40 -0
- package/stacks/supabase/templates/root/src/supabase/requireUser.ts +72 -0
- package/stacks/supabase/templates/root/src/swagger.ts +34 -0
- package/stacks/supabase/templates/root/src/util/assertions.ts +6 -0
- package/stacks/supabase/templates/root/src/util/hash.ts +9 -0
- package/stacks/supabase/templates/root/src/util/sanitize.ts +23 -0
- package/stacks/supabase/templates/root/supabase/config.toml +295 -0
- package/stacks/supabase/templates/root/supabase/migrations/V1__schema.sql.example +12 -0
- package/stacks/supabase/templates/root/supabase/seed.sql +1 -0
- package/stacks/supabase/templates/root/tsconfig.json +32 -0
- package/stacks/supabase/templates/root/vercel.json +8 -0
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md
ADDED
|
@@ -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
|
+
```
|