@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-designing-event-models/SKILL.md
ADDED
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eventmodeling-designing-event-models
|
|
3
|
+
description: "Designs event-sourced domain models. Maps business processes to immutable events and state projections. Events are the source of truth; state is derived from events for command validation. Use when designing event streaming architectures from domain analysis. Do not use for: brainstorming events from scratch (use eventmodeling-brainstorming-events), optimizing stream sizing or snapshotting (use eventmodeling-optimizing-stream-design), or translating external system events (use eventmodeling-translating-external-events)."
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- AskUserQuestion
|
|
6
|
+
- Write
|
|
7
|
+
- Bash
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Designing Event Models
|
|
11
|
+
|
|
12
|
+
> **Before doing anything else**, invoke the `connect` skill to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Then invoke the `learn-eventmodelers-api` skill to load the full API reference. Do not proceed until both skills have been loaded.
|
|
13
|
+
|
|
14
|
+
## Interview Phase (Optional)
|
|
15
|
+
|
|
16
|
+
**When to Interview**: Skip if the user has specified: stream identity strategy, command-specific state needs, and read model requirements. Interview when stream boundaries or state design are unclear.
|
|
17
|
+
|
|
18
|
+
**Interview Strategy**: Establish stream identity and per-command state boundaries before designing. Ambiguous boundaries are the primary cause of the DDD aggregate anti-pattern appearing in event-sourced models.
|
|
19
|
+
|
|
20
|
+
### Critical Questions
|
|
21
|
+
|
|
22
|
+
1. **Stream Identity** (Impact: Determines how events are grouped into streams)
|
|
23
|
+
- Question: "What's the entity that owns events? (e.g., orderId, reviewId, customerId) What's the lifetime? Single transaction or years?"
|
|
24
|
+
- Why it matters: Wrong stream identity causes design problems; correct identity keeps streams appropriately scoped
|
|
25
|
+
- Follow-up triggers: If multiple candidates → ask "Which entity's identity would you use to load events for a single command decision?"
|
|
26
|
+
|
|
27
|
+
2. **Minimal State vs Bundled State** (Impact: Prevents DDD aggregate anti-pattern)
|
|
28
|
+
- Question: "Will different commands need different state? Or does every command need the same full state?"
|
|
29
|
+
- Why it matters: Each command should have minimal, command-specific state—not bundled DDD aggregates
|
|
30
|
+
- Follow-up triggers: If "same full state" → walk through two commands and ask what each actually reads during validation
|
|
31
|
+
|
|
32
|
+
### Interview Flow
|
|
33
|
+
|
|
34
|
+
**Conditional Entry**:
|
|
35
|
+
```text
|
|
36
|
+
If user has provided:
|
|
37
|
+
- Stream identity (which entity ID anchors the stream)
|
|
38
|
+
- AND at least two commands with explicitly different state needs documented
|
|
39
|
+
- AND read model requirements (what queries the UI or processors make)
|
|
40
|
+
|
|
41
|
+
Then: Skip interview, proceed directly to design
|
|
42
|
+
|
|
43
|
+
Else: Conduct interview
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Phase 1: Stream Boundaries** (Question 1)
|
|
47
|
+
- Confirm which entity anchors the stream
|
|
48
|
+
- Establish stream lifetime expectations
|
|
49
|
+
- Identify whether multiple candidate roots exist and resolve them
|
|
50
|
+
|
|
51
|
+
**Phase 2: State Design** (Question 2)
|
|
52
|
+
- Confirm per-command state isolation
|
|
53
|
+
- Identify whether DDD aggregate thinking is present upfront
|
|
54
|
+
- Establish minimal state shapes for at least two commands
|
|
55
|
+
|
|
56
|
+
### Capturing Interview Findings
|
|
57
|
+
|
|
58
|
+
Append findings to the project's event modeling file:
|
|
59
|
+
|
|
60
|
+
**File**: `.trogonai/interviews/[project-name]/EVENTMODELING.md`
|
|
61
|
+
|
|
62
|
+
Use Write tool to add/update this section:
|
|
63
|
+
|
|
64
|
+
```markdown
|
|
65
|
+
## Designing Event Models (eventmodeling-designing-event-models)
|
|
66
|
+
|
|
67
|
+
### Stream Identity
|
|
68
|
+
[From Q1: Which entity? What identity key? Lifetime?]
|
|
69
|
+
|
|
70
|
+
### Per-Command State Decisions
|
|
71
|
+
[From Q2: Which commands need different state? Initial minimal state shapes?]
|
|
72
|
+
|
|
73
|
+
### Design Decisions
|
|
74
|
+
- Stream root: [entity name] identified by [id field]
|
|
75
|
+
- State isolation: [confirmed / DDD pattern caught and corrected]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Update Interview Trail:
|
|
79
|
+
```markdown
|
|
80
|
+
| Design | eventmodeling-designing-event-models | Done | Stream identity, per-command state shapes |
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Core Architectural Rule
|
|
86
|
+
|
|
87
|
+
**NEVER use a "DDD Aggregate Root" (bundled state) for command validation** Every command handler has its own minimal state projection. What DDD calls an "aggregate root" is actually a **read model**, not command-validation state.
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
WRONG: Using DDD Aggregate as command state
|
|
91
|
+
OrderAggregate { orderId, customerId, items[], total, status, paymentId, address, shippedAt, cancelledAt, ... }
|
|
92
|
+
↑ This is a READ MODEL, not command state
|
|
93
|
+
↓ NEVER use for command validation
|
|
94
|
+
handleConfirmOrder(OrderAggregate)
|
|
95
|
+
handleShipOrder(OrderAggregate)
|
|
96
|
+
handleCancelOrder(OrderAggregate)
|
|
97
|
+
|
|
98
|
+
CORRECT: Minimal state per command
|
|
99
|
+
ConfirmOrderState { status, orderId }
|
|
100
|
+
↓
|
|
101
|
+
handleConfirmOrder(ConfirmOrderState)
|
|
102
|
+
|
|
103
|
+
ShipOrderState { status, orderId, paymentId }
|
|
104
|
+
↓
|
|
105
|
+
handleShipOrder(ShipOrderState)
|
|
106
|
+
|
|
107
|
+
CancelOrderState { status, orderId, createdAt }
|
|
108
|
+
↓
|
|
109
|
+
handleCancelOrder(CancelOrderState)
|
|
110
|
+
|
|
111
|
+
OrderSummaryView { orderId, customerId, items[], total, status, paymentId, ... }
|
|
112
|
+
↑ This is for UI queries, NOT command validation
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Purpose
|
|
116
|
+
Converts domain analysis into the event sourcing architecture pattern:
|
|
117
|
+
|
|
118
|
+
**UI/Processor** → **Command** → [Command State Read Model] → **Event** → **Query Read Models** - **UI/Processor**: Entry points that trigger intent
|
|
119
|
+
- **Command**: Intent data (can be rejected)
|
|
120
|
+
- **Stream Root**: Logical grouping of immutable events (NOT a DDD aggregate bundle)
|
|
121
|
+
- **Command State Read Model**: Minimal projection derived from events, optimized for this specific command's validation. Each command gets its own read model interface. Different commands = different shapes. (Categorized as "Command State" for semantic clarity)
|
|
122
|
+
- **Event**: Result of successful command (immutable fact: command data + implicit context)
|
|
123
|
+
- **Query Read Models**: Rich projections of events optimized for UI/Processor queries. Separate from command state. (Categorized as "Query Models" for semantic clarity)
|
|
124
|
+
|
|
125
|
+
## Workflow
|
|
126
|
+
|
|
127
|
+
Given a domain analysis, design a complete event-sourced model:
|
|
128
|
+
|
|
129
|
+
### 1. Design Event Streams
|
|
130
|
+
Events are the immutable source of truth. Each stream holds facts about one entity:
|
|
131
|
+
- **Stream Name**: Entity type + identity (Order:order-123)
|
|
132
|
+
- **Event Type**: What changed? (past tense: Created, Confirmed, Shipped)
|
|
133
|
+
- **Event Data**: Combines command input + implicit stream state facts
|
|
134
|
+
- **Causality**: Triggered by which command?
|
|
135
|
+
|
|
136
|
+
Format:
|
|
137
|
+
```text
|
|
138
|
+
Stream: Order:order-123
|
|
139
|
+
|
|
140
|
+
Events (chronological):
|
|
141
|
+
1. OrderCreated
|
|
142
|
+
Triggered by: CreateOrder command
|
|
143
|
+
Data: customerId, items[], total, shippingAddress, createdAt
|
|
144
|
+
(from command: customerId, items[], shippingAddress)
|
|
145
|
+
(implicit: total calculated from items)
|
|
146
|
+
|
|
147
|
+
2. OrderConfirmed
|
|
148
|
+
Triggered by: ConfirmOrder command
|
|
149
|
+
Data: paymentId, confirmedAt
|
|
150
|
+
(from command: paymentId)
|
|
151
|
+
(implicit: orderId from stream, previous status verified)
|
|
152
|
+
|
|
153
|
+
3. OrderShipped
|
|
154
|
+
Triggered by: ShipOrder command
|
|
155
|
+
Data: shipmentId, shippedAt
|
|
156
|
+
(from command: shipmentId)
|
|
157
|
+
(implicit: orderId, confirmed status verified)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Key Rules**:
|
|
161
|
+
- Events are **immutable facts** from successful commands
|
|
162
|
+
- Event data = command input + implicit stream state context
|
|
163
|
+
- Stream identity is explicit (Order:order-123)
|
|
164
|
+
- Event order matters for state reconstruction
|
|
165
|
+
- Never modify or delete events
|
|
166
|
+
- Events only exist if command succeeded
|
|
167
|
+
|
|
168
|
+
### 2. Design Command State Read Models (Minimal Per-Command)
|
|
169
|
+
|
|
170
|
+
**Critical Rule: Each command must have its own read model (command state). NEVER share read models between commands.** **Naming Convention (for Automation)**:
|
|
171
|
+
- `[CommandName]State` = Implemented command state read model
|
|
172
|
+
- `[CommandName]StateToDo` = Planned command state read model (marked for implementation)
|
|
173
|
+
|
|
174
|
+
Examples:
|
|
175
|
+
- `PublishReviewState` = implemented
|
|
176
|
+
- `EditReviewStateToDo` = planned, needs implementation
|
|
177
|
+
- `SellerRespondState` = implemented
|
|
178
|
+
|
|
179
|
+
**Semantic Categorization**: These are read models, but categorized as "Command State" based on their purpose (command validation, not UI queries).
|
|
180
|
+
|
|
181
|
+
Command state read models are **derived** from events and **minimal**:
|
|
182
|
+
- Read only events needed for a specific command's decision
|
|
183
|
+
- Build state by replaying only relevant events
|
|
184
|
+
- **ENFORCEMENT**: Different commands = different read model interfaces. Period.
|
|
185
|
+
- Each command handler defines what state projection it needs (and ONLY what it needs)
|
|
186
|
+
- Projection can be regenerated from events at any time
|
|
187
|
+
|
|
188
|
+
Example for Order stream with separate command state read model for EACH command:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
## ConfirmOrder Command (IMPLEMENTED)
|
|
192
|
+
State interface: ConfirmOrderState { status, orderId }
|
|
193
|
+
Builder: buildConfirmOrderState(events)
|
|
194
|
+
Naming: [CommandName]State = implemented
|
|
195
|
+
- OrderCreated event → Set status='Draft'
|
|
196
|
+
- OrderConfirmed event → Set status='Confirmed'
|
|
197
|
+
(SKIP: items, total, shipping - not needed for this command)
|
|
198
|
+
|
|
199
|
+
## ShipOrder Command (IMPLEMENTED)
|
|
200
|
+
State interface: ShipOrderState { status, orderId, paymentId }
|
|
201
|
+
Builder: buildShipOrderState(events)
|
|
202
|
+
Naming: [CommandName]State = implemented
|
|
203
|
+
(DIFFERENT from ConfirmOrderState)
|
|
204
|
+
- OrderCreated event → (skip)
|
|
205
|
+
- OrderConfirmed event → Set status='Confirmed', set paymentId
|
|
206
|
+
- OrderShipped event → Set status='Shipped'
|
|
207
|
+
|
|
208
|
+
## CancelOrder Command (PLANNED - NOT IMPLEMENTED)
|
|
209
|
+
State interface: CancelOrderStateToDo { status, orderId, createdAt }
|
|
210
|
+
Builder: buildCancelOrderStateToDo(events) [STUB - TODO]
|
|
211
|
+
Naming: [CommandName]StateToDo = planned, needs implementation
|
|
212
|
+
(DIFFERENT from both above)
|
|
213
|
+
- OrderCreated event → Set status='Draft', createdAt
|
|
214
|
+
- OrderCancelled event → Set status='Cancelled'
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Enforcement Rule**:
|
|
218
|
+
- ConfirmOrderState used ONLY by handleConfirmOrder
|
|
219
|
+
- ShipOrderState used ONLY by handleShipOrder
|
|
220
|
+
- NEVER share state between commands
|
|
221
|
+
- NEVER create a single "OrderState" for all Order commands
|
|
222
|
+
|
|
223
|
+
This is **NOT** a full aggregate state bundle—it's minimal, command-specific state access.
|
|
224
|
+
|
|
225
|
+
### 3. Design Commands
|
|
226
|
+
Commands are **intent data from UI or Processor**:
|
|
227
|
+
- Represent what user/system wants to do
|
|
228
|
+
- Can be rejected (validation failure)
|
|
229
|
+
- Only UI or Processor can issue commands
|
|
230
|
+
- Load current stream state for validation
|
|
231
|
+
- Produce events if valid, or reject if invalid
|
|
232
|
+
|
|
233
|
+
Format:
|
|
234
|
+
```text
|
|
235
|
+
Command: ConfirmOrder
|
|
236
|
+
Source: UI or Processor (only these can issue)
|
|
237
|
+
Input: orderId, paymentId
|
|
238
|
+
|
|
239
|
+
Processing:
|
|
240
|
+
1. Load current state from Order:orderId stream
|
|
241
|
+
2. Validate preconditions:
|
|
242
|
+
- state.status === 'Draft' (reject: already confirmed)
|
|
243
|
+
- paymentId is valid (reject: invalid payment)
|
|
244
|
+
3. If all valid:
|
|
245
|
+
- Produce: OrderConfirmed event
|
|
246
|
+
- Data: paymentId, confirmedAt
|
|
247
|
+
- Implicit: orderId (from stream), previous status (from state)
|
|
248
|
+
4. If any validation fails:
|
|
249
|
+
- Reject: return error (no event created)
|
|
250
|
+
|
|
251
|
+
Outcomes:
|
|
252
|
+
Success: OrderConfirmed event appended to stream
|
|
253
|
+
Rejection: Error returned, no event created
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Key Rules**:
|
|
257
|
+
- Only UI or Processor can issue commands (entry points)
|
|
258
|
+
- One command per UI/Processor action
|
|
259
|
+
- Commands validate against stream state
|
|
260
|
+
- Successful command → Event(s) created
|
|
261
|
+
- Failed validation → Command rejected, no event
|
|
262
|
+
- Commands are synchronous decision logic (pure)
|
|
263
|
+
|
|
264
|
+
### 4. Design Read Models
|
|
265
|
+
Read models are **projections of events for UI/Processor queries**:
|
|
266
|
+
- Built from events (only source is events)
|
|
267
|
+
- Optimized for specific query patterns
|
|
268
|
+
- Consumed by UI or Processor (for display/decision)
|
|
269
|
+
- Can be regenerated from events anytime
|
|
270
|
+
|
|
271
|
+
Format:
|
|
272
|
+
```text
|
|
273
|
+
ReadModel: OrderSummaryView
|
|
274
|
+
Purpose: UI displays customer order list, Processor checks order status
|
|
275
|
+
|
|
276
|
+
Subscribed to events:
|
|
277
|
+
- OrderCreated
|
|
278
|
+
- OrderConfirmed
|
|
279
|
+
- OrderShipped
|
|
280
|
+
- OrderCancelled
|
|
281
|
+
|
|
282
|
+
Data (optimized for queries):
|
|
283
|
+
{
|
|
284
|
+
orderId: string
|
|
285
|
+
customerId: string
|
|
286
|
+
total: number
|
|
287
|
+
status: string
|
|
288
|
+
createdAt: Date
|
|
289
|
+
confirmedAt?: Date
|
|
290
|
+
shippedAt?: Date
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
Update from events:
|
|
294
|
+
- OrderCreated → Insert row (id, customer, total, status='Draft')
|
|
295
|
+
- OrderConfirmed → Update status='Confirmed', set confirmedAt
|
|
296
|
+
- OrderShipped → Update status='Shipped', set shippedAt
|
|
297
|
+
- OrderCancelled → Update status='Cancelled'
|
|
298
|
+
|
|
299
|
+
Consumed by:
|
|
300
|
+
- UI: displays list of orders
|
|
301
|
+
- Processor: checks if order can be shipped
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### 5. Document Event Causality
|
|
305
|
+
Show how events relate to each other:
|
|
306
|
+
|
|
307
|
+
```text
|
|
308
|
+
Command Flow:
|
|
309
|
+
CreateOrder command
|
|
310
|
+
→ OrderCreated event
|
|
311
|
+
↓ (may trigger external process)
|
|
312
|
+
ConfirmOrder command (reads OrderCreated state)
|
|
313
|
+
→ OrderConfirmed event
|
|
314
|
+
↓ (may trigger)
|
|
315
|
+
ShipOrder command (reads OrderCreated + OrderConfirmed state)
|
|
316
|
+
→ OrderShipped event
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 6. Document State Transitions
|
|
320
|
+
Show valid state transitions:
|
|
321
|
+
|
|
322
|
+
```text
|
|
323
|
+
Order Stream State Transitions:
|
|
324
|
+
|
|
325
|
+
Initial state: (empty stream)
|
|
326
|
+
↓
|
|
327
|
+
CreateOrder → OrderCreated
|
|
328
|
+
↓
|
|
329
|
+
State: Draft
|
|
330
|
+
|
|
331
|
+
Draft state:
|
|
332
|
+
→ ConfirmOrder → OrderConfirmed → State: Confirmed
|
|
333
|
+
→ CancelOrder → OrderCancelled → State: Cancelled
|
|
334
|
+
|
|
335
|
+
Confirmed state:
|
|
336
|
+
→ ShipOrder → OrderShipped → State: Shipped
|
|
337
|
+
→ CancelOrder (rejected - already confirmed)
|
|
338
|
+
|
|
339
|
+
Shipped state:
|
|
340
|
+
→ No more transitions allowed
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Output Format
|
|
344
|
+
|
|
345
|
+
Present complete model as:
|
|
346
|
+
|
|
347
|
+
```markdown
|
|
348
|
+
# Event Model: [Domain]
|
|
349
|
+
|
|
350
|
+
## Event Streams
|
|
351
|
+
|
|
352
|
+
### Stream: Order
|
|
353
|
+
|
|
354
|
+
**Identity**: orderId
|
|
355
|
+
|
|
356
|
+
**Events**:
|
|
357
|
+
- OrderCreated: Initial event creating the order
|
|
358
|
+
Data: customerId, items[], total, shippingAddress
|
|
359
|
+
|
|
360
|
+
- OrderConfirmed: Payment confirmed
|
|
361
|
+
Data: paymentId, confirmedAt
|
|
362
|
+
|
|
363
|
+
- OrderShipped: Order shipped
|
|
364
|
+
Data: shipmentId, shippedAt
|
|
365
|
+
|
|
366
|
+
- OrderCancelled: Order cancelled
|
|
367
|
+
Data: cancelledAt, reason
|
|
368
|
+
|
|
369
|
+
**State Projection (Human Example)**:
|
|
370
|
+
For the ConfirmOrder command, we need minimal state:
|
|
371
|
+
```text
|
|
372
|
+
ConfirmOrderState:
|
|
373
|
+
- orderId: 'order-123'
|
|
374
|
+
- status: 'Draft'
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
For the ShipOrder command, we need different data:
|
|
378
|
+
```text
|
|
379
|
+
ShipOrderState:
|
|
380
|
+
- orderId: 'order-123'
|
|
381
|
+
- status: 'Confirmed'
|
|
382
|
+
- paymentId: 'payment-456'
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Commands
|
|
388
|
+
|
|
389
|
+
### Command: CreateOrder
|
|
390
|
+
- Input: customerId, items[], shippingAddress
|
|
391
|
+
- Validation: Items valid, customerId exists
|
|
392
|
+
- Events produced: OrderCreated
|
|
393
|
+
- Possible outcomes: Success (OrderCreated) or Validation error
|
|
394
|
+
|
|
395
|
+
### Command: ConfirmOrder
|
|
396
|
+
- Input: orderId, paymentId
|
|
397
|
+
- Validation: Order in Draft status, payment validated
|
|
398
|
+
- Events produced: OrderConfirmed
|
|
399
|
+
- Possible outcomes: Success or "Already confirmed" error
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Read Models (Optional)
|
|
404
|
+
|
|
405
|
+
### ReadModel: OrderSummaryView
|
|
406
|
+
- Purpose: Quick lookup of order status
|
|
407
|
+
- Events: OrderCreated, OrderConfirmed, OrderShipped, OrderCancelled
|
|
408
|
+
- Queries served: GetOrder(orderId), ListOrdersByCustomer(customerId)
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Implementation Notes
|
|
413
|
+
- All state is derived from events
|
|
414
|
+
- Commands validate against derived state
|
|
415
|
+
- No transaction across streams
|
|
416
|
+
- Events are source of truth
|
|
417
|
+
- Read models can be rebuilt from events
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Key Event Sourcing Principles
|
|
421
|
+
|
|
422
|
+
1. **Events are Facts**: Events describe what happened, not what might happen
|
|
423
|
+
2. **Immutable Event Log**: Events are appended, never modified
|
|
424
|
+
3. **State is Minimal and Command-Driven**: State is built by replaying events, but ONLY for what a specific command needs to validate. Not all stream fields are needed for all commands.
|
|
425
|
+
4. **Not DDD Aggregates**: Stream roots group events logically, but aren't bundles of related data like DDD aggregates. State is determined per-command, not designed upfront for the whole stream.
|
|
426
|
+
5. **Commands are Pure**: No side effects, just decision logic against minimal state
|
|
427
|
+
6. **Read Models are Separate**: Read models (projections) are separate from command-validation state. Read models can have rich data; command state stays minimal.
|
|
428
|
+
7. **Event Causality**: Commands → [minimal state] → Events → [read models]
|
|
429
|
+
|
|
430
|
+
## Design Patterns
|
|
431
|
+
|
|
432
|
+
### Compensation Pattern
|
|
433
|
+
Handle errors by appending compensation events:
|
|
434
|
+
```text
|
|
435
|
+
Command: ProcessPayment failed
|
|
436
|
+
→ PaymentFailed event
|
|
437
|
+
(triggered by external error)
|
|
438
|
+
→ OrderCancelled event (compensation)
|
|
439
|
+
(or retry logic)
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Temporal Queries
|
|
443
|
+
Answer "what was the state at time T?":
|
|
444
|
+
```text
|
|
445
|
+
Replay events up to timestamp T
|
|
446
|
+
→ Get historical state
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Best Practices for Event Model Design
|
|
450
|
+
|
|
451
|
+
### 1. Design Minimal State Per Command
|
|
452
|
+
Each command handler only loads the state it needs:
|
|
453
|
+
- "LoadOrderState loads { id, items, total, shipping, customer, payment, status, ... }"
|
|
454
|
+
- "ConfirmOrderState loads { status, orderId }"
|
|
455
|
+
- "ShipOrderState loads { status, orderId, paymentConfirmed }"
|
|
456
|
+
|
|
457
|
+
### 2. Separate Command State from Query Models
|
|
458
|
+
Keep command-validation state and read models strictly separate:
|
|
459
|
+
- **Command State** (minimal): Used by handlers to validate commands
|
|
460
|
+
- **Query Models** (rich): Used by UI/Processor to display/query data
|
|
461
|
+
- Never share between them
|
|
462
|
+
|
|
463
|
+
### 3. Name State Interfaces by Command
|
|
464
|
+
Use the pattern `[CommandName]State` to make the relationship explicit:
|
|
465
|
+
- `PublishReviewState` for PublishReview command
|
|
466
|
+
- `EditReviewState` for EditReview command
|
|
467
|
+
- `ReviewState` (ambiguous - which command?)
|
|
468
|
+
|
|
469
|
+
### 4. Document State Transitions Clearly
|
|
470
|
+
Show what state changes trigger what commands:
|
|
471
|
+
- Include initial state
|
|
472
|
+
- Show all valid transitions
|
|
473
|
+
- Mark impossible transitions (and why)
|
|
474
|
+
- Document rejection conditions
|
|
475
|
+
|
|
476
|
+
### 5. Make All Constraints Explicit
|
|
477
|
+
Transform "obvious" business rules into documented invariants:
|
|
478
|
+
- "Obviously can't ship an unconfirmed order"
|
|
479
|
+
- "ShipOrder validation: requires status='Confirmed' with paymentId"
|
|
480
|
+
|
|
481
|
+
### 6. Keep Event Data Factual
|
|
482
|
+
Events record facts, not derived values:
|
|
483
|
+
- "OrderCreated { items, total }" (total is computed from items)
|
|
484
|
+
- "OrderCreated { items[] with unitPrice, shippingAddress }" (total computed in projection)
|
|
485
|
+
|
|
486
|
+
## Quality Checklist
|
|
487
|
+
|
|
488
|
+
- [ ] All events are immutable facts (past tense)
|
|
489
|
+
- [ ] Events contain only captured data, no computed fields
|
|
490
|
+
- [ ] State projection is deterministic from events
|
|
491
|
+
- [ ] Each command validates against current state
|
|
492
|
+
- [ ] Commands either produce events or reject
|
|
493
|
+
- [ ] Event causality is clear
|
|
494
|
+
- [ ] State transitions are documented
|
|
495
|
+
- [ ] No references between streams in events
|
|
496
|
+
- [ ] Read models are optional, not required
|
|
497
|
+
- [ ] All logic is state → events (pure functions)
|