@eventmodelers/cli 1.0.36 → 1.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/shared/skills/learn-eventmodelers-api/SKILL.md +22 -13
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +2 -23
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/references/api-fallback.md +11 -0
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +6 -57
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/references/api-fallback.md +68 -0
- package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +4 -61
- package/stacks/modeling-kit/templates/.claude/skills/attributes/references/api-fallback.md +39 -0
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +9 -53
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md +63 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +12 -380
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/references/examples.md +329 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +45 -290
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/api-fallback.md +97 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/examples.md +35 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +15 -410
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/api-fallback.md +22 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/examples.md +397 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-core-rules/SKILL.md +182 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/SKILL.md +148 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/references/api-fallback.md +21 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +48 -448
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/references/examples.md +167 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +31 -380
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +31 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/examples.md +216 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +33 -393
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +79 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/examples.md +282 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +54 -454
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +67 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/examples.md +273 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-interview-protocol/SKILL.md +31 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +37 -184
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +58 -99
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +82 -163
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +86 -78
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +51 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +5 -5
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +10 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +22 -51
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +41 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +12 -9
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +1 -10
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +29 -382
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +77 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/examples.md +271 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +12 -349
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/references/examples.md +306 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +69 -107
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +75 -160
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +3 -110
- package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +118 -0
- package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +5 -25
- package/stacks/modeling-kit/templates/.claude/skills/handle-comment/references/api-fallback.md +35 -0
- package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +9 -44
- package/stacks/modeling-kit/templates/.claude/skills/html-screen/references/api-fallback.md +51 -0
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +34 -180
- package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +203 -0
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +14 -81
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +74 -0
- package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +4 -45
- package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/references/api-fallback.md +44 -0
- package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +19 -88
- package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +91 -0
- package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +1 -9
- package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +0 -674
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +0 -204
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: eventmodeling-designing-event-models
|
|
3
|
-
description: "Designs event
|
|
3
|
+
description: "Designs the event model for a business process — maps Commands, Events, and Read Models with clear causality. Events are business facts; Read Models are projections built from them. Use when designing the Command/Event/Read-Model structure from domain analysis. Do not use for: brainstorming events from scratch (use eventmodeling-brainstorming-events), or checking whether an event belongs to the right entity/timeline (use eventmodeling-optimizing-stream-design)."
|
|
4
4
|
allowed-tools:
|
|
5
5
|
- AskUserQuestion
|
|
6
6
|
- Write
|
|
@@ -11,487 +11,87 @@ allowed-tools:
|
|
|
11
11
|
|
|
12
12
|
> **Before doing anything else**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until it has completed. Consult `learn-eventmodelers-api` only if you need to look up a specific endpoint or field this file doesn't cover — don't load it eagerly.
|
|
13
13
|
|
|
14
|
-
|
|
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
|
-
```
|
|
14
|
+
This step applies the shared element rules in **`eventmodeling-core-rules`** — read it once per session if you haven't already; it defines what a COMMAND/EVENT/READMODEL/SCREEN/AUTOMATION is, how each is named, and the anti-patterns to reject, so this step doesn't restate them.
|
|
114
15
|
|
|
115
16
|
## Purpose
|
|
116
|
-
Converts domain analysis into the event sourcing architecture pattern:
|
|
117
17
|
|
|
118
|
-
|
|
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)
|
|
18
|
+
Converts domain analysis into the event modeling structure:
|
|
124
19
|
|
|
125
|
-
|
|
20
|
+
**UI/Processor** → **Command** → **Event** → **Read Model** → **UI/Processor**
|
|
126
21
|
|
|
127
|
-
|
|
22
|
+
- **UI/Processor**: entry points that trigger intent
|
|
23
|
+
- **Command**: business intent, expressed in business language, can be rejected
|
|
24
|
+
- **Event**: a business fact — the result of a successful command
|
|
25
|
+
- **Read Model**: a projection of events, shaped for a specific query need
|
|
128
26
|
|
|
129
|
-
|
|
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?
|
|
27
|
+
## Workflow
|
|
135
28
|
|
|
136
|
-
|
|
137
|
-
```text
|
|
138
|
-
Stream: Order:order-123
|
|
29
|
+
Given a domain analysis, design the event model:
|
|
139
30
|
|
|
140
|
-
Events
|
|
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)
|
|
31
|
+
### 1. Design Events
|
|
146
32
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
(implicit: orderId from stream, previous status verified)
|
|
33
|
+
Events are business facts, not technical occurrences:
|
|
34
|
+
- **Event Type**: what changed, in past tense business language (Created, Confirmed, Shipped)
|
|
35
|
+
- **Event Data**: the business fields, noting which came directly from the triggering command versus which are implicit context (e.g. a calculated total, or the entity's identity)
|
|
36
|
+
- **Causality**: which command triggered it
|
|
152
37
|
|
|
153
|
-
|
|
154
|
-
Triggered by: ShipOrder command
|
|
155
|
-
Data: shipmentId, shippedAt
|
|
156
|
-
(from command: shipmentId)
|
|
157
|
-
(implicit: orderId, confirmed status verified)
|
|
158
|
-
```
|
|
38
|
+
Document each entity's events as a chronological list, giving each event's triggering command and its data. A full worked example (an Order) is in `references/examples.md`.
|
|
159
39
|
|
|
160
40
|
**Key Rules**:
|
|
161
|
-
- Events are
|
|
162
|
-
-
|
|
163
|
-
-
|
|
164
|
-
-
|
|
165
|
-
-
|
|
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
|
|
41
|
+
- Events are immutable business facts
|
|
42
|
+
- Events use past tense, business language
|
|
43
|
+
- Event order matters — it tells the story
|
|
44
|
+
- Never modify or delete an event once it exists
|
|
45
|
+
- Events only exist if the triggering command succeeded
|
|
222
46
|
|
|
223
|
-
|
|
47
|
+
### 2. Design Commands
|
|
224
48
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
- Represent what user/system wants to do
|
|
49
|
+
Commands are business intent from a UI or Processor:
|
|
50
|
+
- Represent what a user or system wants to do
|
|
228
51
|
- Can be rejected (validation failure)
|
|
229
|
-
- Only UI or Processor can issue
|
|
230
|
-
- Load current stream state for validation
|
|
52
|
+
- Only a UI or Processor can issue a command
|
|
231
53
|
- Produce events if valid, or reject if invalid
|
|
232
54
|
|
|
233
|
-
|
|
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
|
-
```
|
|
55
|
+
Document each command's source, its input fields, the preconditions it's checked against, the event(s) it produces on success (noting which data comes from the command versus implicit context), and its rejection outcomes. A full worked example (ConfirmOrder) is in `references/examples.md`.
|
|
255
56
|
|
|
256
57
|
**Key Rules**:
|
|
257
|
-
- Only UI or Processor can issue commands (entry points)
|
|
58
|
+
- Only a UI or Processor can issue commands (entry points)
|
|
258
59
|
- One command per UI/Processor action
|
|
259
|
-
- Commands validate against stream state
|
|
260
60
|
- Successful command → Event(s) created
|
|
261
61
|
- 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
62
|
|
|
293
|
-
|
|
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'
|
|
63
|
+
### 3. Design Read Models
|
|
298
64
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
65
|
+
Read models are projections of events, built for UI/Processor queries:
|
|
66
|
+
- Built from events (their only source)
|
|
67
|
+
- Optimized for a specific query pattern
|
|
68
|
+
- Consumed by UI (for display) or Processor (for a decision)
|
|
69
|
+
- Can be regenerated from events at any time
|
|
303
70
|
|
|
304
|
-
|
|
305
|
-
Show how events relate to each other:
|
|
71
|
+
Document each read model's purpose, which events it subscribes to, its query-shaped data, how each subscribed event updates that data, and who consumes it. A full worked example (OrderSummaryView) is in `references/examples.md`.
|
|
306
72
|
|
|
307
|
-
|
|
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
|
-
```
|
|
73
|
+
### 4. Document Event Causality
|
|
318
74
|
|
|
319
|
-
|
|
320
|
-
Show valid state transitions:
|
|
75
|
+
Trace the causal chain from the first command through the events and downstream commands it can trigger, showing how a later command's decision depends on events produced earlier. A full worked example (an Order create→confirm→ship chain) is in `references/examples.md`.
|
|
321
76
|
|
|
322
|
-
|
|
323
|
-
Order Stream State Transitions:
|
|
77
|
+
### 5. Document State Transitions
|
|
324
78
|
|
|
325
|
-
|
|
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
|
-
```
|
|
79
|
+
Map the valid states a process can be in, which command causes which transition, and which transitions are invalid from a given state (e.g. cancelling an already-shipped order), ending at any terminal states. A full worked example (Order transitions) is in `references/examples.md`.
|
|
342
80
|
|
|
343
81
|
### Output Format
|
|
344
82
|
|
|
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
|
|
83
|
+
Present the complete model as a markdown document with sections for Events (per entity, chronological, each with its triggering command), Commands (input, preconditions, events produced, rejection outcomes), and Read Models (purpose, subscribed events, consumers). A full worked example of this document structure is in `references/examples.md`.
|
|
475
84
|
|
|
476
|
-
|
|
477
|
-
Transform "obvious" business rules into documented invariants:
|
|
478
|
-
- "Obviously can't ship an unconfirmed order"
|
|
479
|
-
- "ShipOrder validation: requires status='Confirmed' with paymentId"
|
|
85
|
+
## Key Principles
|
|
480
86
|
|
|
481
|
-
|
|
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)
|
|
87
|
+
See `eventmodeling-core-rules` for the element definitions and causality rules this design must follow (events are immutable facts, commands are checked against documented preconditions, read models never drive command validation).
|
|
485
88
|
|
|
486
89
|
## Quality Checklist
|
|
487
90
|
|
|
488
|
-
- [ ] All events are immutable facts (past tense)
|
|
489
|
-
- [ ] Events contain only captured data, no computed fields
|
|
490
|
-
- [ ]
|
|
491
|
-
- [ ]
|
|
492
|
-
- [ ]
|
|
493
|
-
- [ ]
|
|
494
|
-
- [ ]
|
|
495
|
-
- [ ] No references between streams in events
|
|
496
|
-
- [ ] Read models are optional, not required
|
|
497
|
-
- [ ] All logic is state → events (pure functions)
|
|
91
|
+
- [ ] All events are immutable business facts (past tense)
|
|
92
|
+
- [ ] Events contain only captured business data, no computed fields
|
|
93
|
+
- [ ] Each command's preconditions are documented explicitly
|
|
94
|
+
- [ ] Commands either produce events or are rejected — no silent failures
|
|
95
|
+
- [ ] Event causality is clear and traceable end to end
|
|
96
|
+
- [ ] State transitions are documented, including which ones are invalid and why
|
|
97
|
+
- [ ] Read models are optional — nothing requires one to exist
|