eventmodeler 0.4.6 → 0.5.0
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/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +3 -0
- package/dist/eventmodeler.js +3 -2
- package/dist/index.js +58 -13
- package/dist/slices/guide/guides/codegen.d.ts +5 -0
- package/dist/slices/guide/guides/codegen.js +339 -0
- package/dist/slices/guide/guides/connect-slices.d.ts +5 -0
- package/dist/slices/guide/guides/connect-slices.js +202 -0
- package/dist/slices/guide/guides/create-slices.d.ts +5 -0
- package/dist/slices/guide/guides/create-slices.js +303 -0
- package/dist/slices/guide/guides/explore.d.ts +5 -0
- package/dist/slices/guide/guides/explore.js +251 -0
- package/dist/slices/guide/guides/information-flow.d.ts +5 -0
- package/dist/slices/guide/guides/information-flow.js +318 -0
- package/dist/slices/guide/guides/scenarios.d.ts +5 -0
- package/dist/slices/guide/guides/scenarios.js +269 -0
- package/dist/slices/guide/index.d.ts +1 -0
- package/dist/slices/guide/index.js +40 -0
- package/dist/slices/open-app/index.js +1 -1
- package/package.json +5 -10
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'explore',
|
|
3
|
+
description: 'Navigate and query an event model: list, show, search, completeness',
|
|
4
|
+
};
|
|
5
|
+
export const content = `
|
|
6
|
+
# Exploring Event Models with the CLI
|
|
7
|
+
|
|
8
|
+
The \`eventmodeler\` CLI lets you query and explore event models linked to your project.
|
|
9
|
+
|
|
10
|
+
## Getting an Overview
|
|
11
|
+
|
|
12
|
+
**Start here** to understand the model:
|
|
13
|
+
|
|
14
|
+
\`\`\`bash
|
|
15
|
+
eventmodeler summary
|
|
16
|
+
\`\`\`
|
|
17
|
+
Shows statistics: total slices, events, commands, read models, etc.
|
|
18
|
+
|
|
19
|
+
\`\`\`bash
|
|
20
|
+
eventmodeler list slices
|
|
21
|
+
\`\`\`
|
|
22
|
+
Lists all slices with their status (created, in-progress, blocked, done). Slices represent vertical features.
|
|
23
|
+
|
|
24
|
+
\`\`\`bash
|
|
25
|
+
eventmodeler list slices --chapter "<name>"
|
|
26
|
+
\`\`\`
|
|
27
|
+
Lists only slices within a specific chapter.
|
|
28
|
+
|
|
29
|
+
\`\`\`bash
|
|
30
|
+
eventmodeler list chapters
|
|
31
|
+
\`\`\`
|
|
32
|
+
Lists chapters - timeline sections that group slices.
|
|
33
|
+
|
|
34
|
+
## Listing Elements
|
|
35
|
+
|
|
36
|
+
\`\`\`bash
|
|
37
|
+
eventmodeler list events # All events in the model
|
|
38
|
+
eventmodeler list commands # All commands in the model
|
|
39
|
+
eventmodeler list readmodels # All read models
|
|
40
|
+
eventmodeler list screens # All screens
|
|
41
|
+
eventmodeler list processors # All processors
|
|
42
|
+
eventmodeler list scenarios # All scenarios
|
|
43
|
+
eventmodeler list aggregates # All aggregates (event groupings)
|
|
44
|
+
eventmodeler list actors # All actors (user/system roles)
|
|
45
|
+
\`\`\`
|
|
46
|
+
|
|
47
|
+
All \`list\` commands support \`--format xml|json\`.
|
|
48
|
+
|
|
49
|
+
## Searching
|
|
50
|
+
|
|
51
|
+
\`\`\`bash
|
|
52
|
+
eventmodeler search "<term>"
|
|
53
|
+
\`\`\`
|
|
54
|
+
Searches across all entity names: slices, events, commands, read models, aggregates, and actors.
|
|
55
|
+
|
|
56
|
+
## Drilling Into Details
|
|
57
|
+
|
|
58
|
+
### Show a Slice
|
|
59
|
+
\`\`\`bash
|
|
60
|
+
eventmodeler show slice "<name>"
|
|
61
|
+
\`\`\`
|
|
62
|
+
Returns XML with:
|
|
63
|
+
- All components in the slice (screens, commands, events, read-models, processors)
|
|
64
|
+
- **Inline flow annotations** on each component showing \`<flows-from>\` and \`<flows-to>\` connections
|
|
65
|
+
- Fields on each component with types and attributes
|
|
66
|
+
- Information flow (which components connect to which)
|
|
67
|
+
- **Inbound flows**: External elements flowing INTO this slice (with field mappings)
|
|
68
|
+
- **Outbound flows**: Elements in this slice flowing OUT to other slices (with field mappings)
|
|
69
|
+
- Scenarios (Given-When-Then test cases)
|
|
70
|
+
|
|
71
|
+
The inbound/outbound flows show cross-slice dependencies - crucial for understanding how a slice integrates with the rest of the system.
|
|
72
|
+
|
|
73
|
+
**Scenarios are key** - they define the slice's behavior and give it context. A slice's scenarios show:
|
|
74
|
+
- **Given**: What events have already occurred (the starting state)
|
|
75
|
+
- **When**: What command is being executed with what field values
|
|
76
|
+
- **Then**: The expected outcome - either:
|
|
77
|
+
- Events that should be produced
|
|
78
|
+
- An error that should occur
|
|
79
|
+
- A read model assertion (expected state)
|
|
80
|
+
|
|
81
|
+
Example scenario in slice output:
|
|
82
|
+
\`\`\`xml
|
|
83
|
+
<scenarios>
|
|
84
|
+
<scenario name="Successfully place order">
|
|
85
|
+
<given>
|
|
86
|
+
<event type="CustomerRegistered">customerId: "123"</event>
|
|
87
|
+
</given>
|
|
88
|
+
<when>
|
|
89
|
+
<command type="PlaceOrder">customerId: "123", items: [...]</command>
|
|
90
|
+
</when>
|
|
91
|
+
<then>
|
|
92
|
+
<event type="OrderPlaced">orderId: "456", customerId: "123"</event>
|
|
93
|
+
</then>
|
|
94
|
+
</scenario>
|
|
95
|
+
<scenario name="Cannot place order without items">
|
|
96
|
+
<when>
|
|
97
|
+
<command type="PlaceOrder">customerId: "123", items: []</command>
|
|
98
|
+
</when>
|
|
99
|
+
<then>
|
|
100
|
+
<error type="ValidationError">Order must contain at least one item</error>
|
|
101
|
+
</then>
|
|
102
|
+
</scenario>
|
|
103
|
+
</scenarios>
|
|
104
|
+
\`\`\`
|
|
105
|
+
|
|
106
|
+
Reading scenarios helps understand:
|
|
107
|
+
- What preconditions the slice expects
|
|
108
|
+
- What inputs the command accepts
|
|
109
|
+
- What the happy path looks like
|
|
110
|
+
- What error cases are handled
|
|
111
|
+
|
|
112
|
+
### Show an Event or Command
|
|
113
|
+
\`\`\`bash
|
|
114
|
+
eventmodeler show event "<name>"
|
|
115
|
+
eventmodeler show command "<name>"
|
|
116
|
+
\`\`\`
|
|
117
|
+
Returns XML with the element's fields and connections.
|
|
118
|
+
|
|
119
|
+
### Show Read Models, Screens, and Processors
|
|
120
|
+
\`\`\`bash
|
|
121
|
+
eventmodeler show readmodel "<name>"
|
|
122
|
+
eventmodeler show screen "<name>"
|
|
123
|
+
eventmodeler show processor "<name>"
|
|
124
|
+
\`\`\`
|
|
125
|
+
Use these for detailed flow context:
|
|
126
|
+
- \`show readmodel\`: fields, source events, and who consumes it
|
|
127
|
+
- \`show screen\`: fields, source read models, and triggered commands
|
|
128
|
+
- \`show processor\`: fields, source read models, and triggered commands
|
|
129
|
+
|
|
130
|
+
### Show Aggregate and Scenario
|
|
131
|
+
\`\`\`bash
|
|
132
|
+
eventmodeler show aggregate "<name>"
|
|
133
|
+
eventmodeler show scenario "<name>"
|
|
134
|
+
\`\`\`
|
|
135
|
+
- \`show aggregate\`: aggregate ID configuration and contained events
|
|
136
|
+
- \`show scenario\`: complete Given-When-Then with field values
|
|
137
|
+
|
|
138
|
+
### Show a Chapter
|
|
139
|
+
\`\`\`bash
|
|
140
|
+
eventmodeler show chapter "<name>"
|
|
141
|
+
\`\`\`
|
|
142
|
+
Shows the chapter with:
|
|
143
|
+
- All slices within the chapter
|
|
144
|
+
- **Flow graph**: How slices connect to each other (which events flow to which read models, etc.) with field mappings
|
|
145
|
+
- **External dependencies**:
|
|
146
|
+
- Inbound: Flows from outside the chapter into slices within it
|
|
147
|
+
- Outbound: Flows from slices in this chapter to elements outside it
|
|
148
|
+
|
|
149
|
+
Use \`show chapter\` to understand the relationships between slices in a feature area. Use \`list slices --chapter\` for a simple list without flow details.
|
|
150
|
+
|
|
151
|
+
### Show an Actor
|
|
152
|
+
\`\`\`bash
|
|
153
|
+
eventmodeler show actor "<name>"
|
|
154
|
+
\`\`\`
|
|
155
|
+
Shows the actor and lists all screens associated with it.
|
|
156
|
+
|
|
157
|
+
## Managing Slice Status
|
|
158
|
+
|
|
159
|
+
Slices have a status that tracks their progress: \`created\`, \`in-progress\`, \`blocked\`, \`done\`.
|
|
160
|
+
|
|
161
|
+
\`\`\`bash
|
|
162
|
+
# Mark a slice as in progress
|
|
163
|
+
eventmodeler mark "Place Order" in-progress
|
|
164
|
+
|
|
165
|
+
# Mark a slice as done
|
|
166
|
+
eventmodeler mark "Place Order" done
|
|
167
|
+
|
|
168
|
+
# Mark a slice as blocked
|
|
169
|
+
eventmodeler mark "Place Order" blocked
|
|
170
|
+
|
|
171
|
+
# Reset to created
|
|
172
|
+
eventmodeler mark "Place Order" created
|
|
173
|
+
\`\`\`
|
|
174
|
+
|
|
175
|
+
Use \`list slices\` to see current statuses.
|
|
176
|
+
|
|
177
|
+
## Checking Information Completeness
|
|
178
|
+
|
|
179
|
+
Information completeness tracks whether fields flow correctly between connected elements.
|
|
180
|
+
|
|
181
|
+
### Check a Specific Element
|
|
182
|
+
\`\`\`bash
|
|
183
|
+
eventmodeler show completeness "<name>"
|
|
184
|
+
\`\`\`
|
|
185
|
+
Auto-detects the element type (command, event, read model, screen, or processor) and shows:
|
|
186
|
+
- Incoming flows to that element
|
|
187
|
+
- Which fields are satisfied (have a source)
|
|
188
|
+
- Which fields are unsatisfied (missing a source)
|
|
189
|
+
|
|
190
|
+
### Check the Entire Model
|
|
191
|
+
\`\`\`bash
|
|
192
|
+
eventmodeler show model-completeness
|
|
193
|
+
\`\`\`
|
|
194
|
+
Project-wide view of all information flows:
|
|
195
|
+
- Summary: complete count, incomplete count, total
|
|
196
|
+
- Lists all incomplete flows with their unsatisfied fields
|
|
197
|
+
|
|
198
|
+
### Check Aggregate ID Fields
|
|
199
|
+
\`\`\`bash
|
|
200
|
+
eventmodeler show aggregate-completeness "<aggregate-name>"
|
|
201
|
+
\`\`\`
|
|
202
|
+
Checks if all events in an aggregate have the aggregate's ID field.
|
|
203
|
+
|
|
204
|
+
## Using Element IDs Instead of Names
|
|
205
|
+
|
|
206
|
+
Every command that accepts an element name also accepts an element ID with the \`id:\` prefix. This works everywhere: \`show\`, \`add field\`, \`remove field\`, \`update field\`, \`add scenario\`, \`remove scenario\`, \`create flow\`, \`remove flow\`, \`map fields\`, \`mark\`, \`codegen\`, and \`search\`.
|
|
207
|
+
|
|
208
|
+
\`\`\`bash
|
|
209
|
+
# Use a full UUID
|
|
210
|
+
eventmodeler show event "id:abc12345-1234-5678-9abc-def012345678"
|
|
211
|
+
|
|
212
|
+
# Or just a unique prefix (first 8 chars is usually enough)
|
|
213
|
+
eventmodeler show event "id:abc12345"
|
|
214
|
+
|
|
215
|
+
# Works with any command
|
|
216
|
+
eventmodeler add field "id:abc12345" --xml '<field name="status" type="String"/>'
|
|
217
|
+
eventmodeler update field "id:abc12345" --field "status" --optional true
|
|
218
|
+
eventmodeler create flow --from "id:abc12345" --to "OrderSummary"
|
|
219
|
+
\`\`\`
|
|
220
|
+
|
|
221
|
+
This is essential when multiple elements share the same name (e.g., linked copies). The CLI will tell you when names are ambiguous and show the IDs to use. Find element IDs via \`list\` commands:
|
|
222
|
+
|
|
223
|
+
\`\`\`bash
|
|
224
|
+
eventmodeler list events # shows id="..." for each event
|
|
225
|
+
eventmodeler list commands # shows id="..." for each command
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
## Exploration Strategy
|
|
229
|
+
|
|
230
|
+
When exploring an unfamiliar event model:
|
|
231
|
+
|
|
232
|
+
1. **Get the big picture**: \`eventmodeler summary\` → understand scale
|
|
233
|
+
2. **See the features**: \`eventmodeler list slices\` → what does this system do?
|
|
234
|
+
3. **Understand organization**: \`eventmodeler list chapters\` → how is it structured over time?
|
|
235
|
+
4. **Dive into a slice**: \`eventmodeler show slice "<name>"\` → how does one feature work?
|
|
236
|
+
5. **Read the scenarios**: They tell you what the slice actually does, its preconditions, and edge cases
|
|
237
|
+
6. **Search for specifics**: \`eventmodeler search "<term>"\` → find related elements
|
|
238
|
+
|
|
239
|
+
When checking model health:
|
|
240
|
+
1. **Overall completeness**: \`eventmodeler show model-completeness\` → any broken flows?
|
|
241
|
+
2. **Specific element**: \`eventmodeler show completeness "<name>"\` → what's missing here?
|
|
242
|
+
3. **Aggregate consistency**: \`eventmodeler show aggregate-completeness "<name>"\` → ID fields present?
|
|
243
|
+
|
|
244
|
+
When looking for something specific:
|
|
245
|
+
- Know the feature name? → \`show slice\`
|
|
246
|
+
- Know an event name? → \`show event\` or \`search\`
|
|
247
|
+
- Know a read model/screen/processor name? → \`show readmodel\` / \`show screen\` / \`show processor\`
|
|
248
|
+
- Need scenario details? → \`list scenarios\` then \`show scenario\`
|
|
249
|
+
- Want all events? → \`list events\`
|
|
250
|
+
- Checking data flow? → \`show completeness\` or \`show model-completeness\`
|
|
251
|
+
`;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const meta: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const content = "\n# Information Flow: Fields, Mappings, and Completeness\n\nThis guide covers the full lifecycle of information in an event model: designing fields, adding them to elements, mapping data between connected elements, updating field properties, and verifying completeness.\n\n---\n\n## Designing Read Models\n\nRead models are projections of event data - views that serve specific screens or use cases. Every field in a read model should trace back to event fields.\n\n### Understand the Context\n```bash\n# Look at the slice containing the read model\neventmodeler show slice \"<slice-name>\"\n\n# Check current completeness - what's missing?\neventmodeler show completeness \"<read-model-name>\"\n\n# See what events feed into this read model\neventmodeler list events\n```\n\n### Design the Fields\nBased on the use case, identify what fields are needed:\n- **Identity fields** - IDs to identify the entity\n- **Display fields** - What the user sees\n- **Status fields** - Current state\n- **Computed fields** - Aggregations, counts, derived values\n- **Timestamp fields** - When things happened\n\n### Propose Before Adding\n```\nI suggest these fields for the OrderHistory read model:\n- orderId (UUID) - identifies the order\n- customerId (UUID) - who placed it\n- status (String) - current status: pending, shipped, delivered, cancelled\n- total (Decimal) - order total\n- placedAt (DateTime) - when it was placed\n- lastUpdatedAt (DateTime) - when status last changed\n\nDoes this look right?\n```\n\n## Adding Fields\n\nField types: `UUID`, `String`, `Int`, `Long`, `Double`, `Decimal`, `Boolean`, `Date`, `DateTime`, `Custom`\n\n```bash\n# Add a simple field\neventmodeler add field --read-model \"OrderSummary\" --xml '<field name=\"orderId\" type=\"UUID\"/>'\n\n# Add an optional field\neventmodeler add field --read-model \"OrderSummary\" --xml '<field name=\"cancelledAt\" type=\"DateTime\" isOptional=\"true\"/>'\n\n# Add a list field\neventmodeler add field --read-model \"OrderSummary\" --xml '<field name=\"itemIds\" type=\"UUID\" isList=\"true\"/>'\n\n# Add a complex/nested field\neventmodeler add field --read-model \"OrderSummary\" --xml '<field name=\"shippingAddress\" type=\"Custom\">\n <field name=\"street\" type=\"String\"/>\n <field name=\"city\" type=\"String\"/>\n <field name=\"postalCode\" type=\"String\"/>\n</field>'\n```\n\n**CRITICAL: Custom fields MUST contain subfields.** A Custom field without subfields is invalid.\n\n### Lists and Custom Types\n\n**List of custom objects** - combine `type=\"Custom\"` with `isList=\"true\"`:\n```bash\neventmodeler add field --read-model \"OrderSummary\" --xml '<field name=\"lineItems\" type=\"Custom\" isList=\"true\">\n <field name=\"productId\" type=\"UUID\"/>\n <field name=\"productName\" type=\"String\"/>\n <field name=\"quantity\" type=\"Int\"/>\n <field name=\"unitPrice\" type=\"Decimal\"/>\n</field>'\n```\n\n**Nested custom types** - subfields can themselves be Custom:\n```bash\neventmodeler add field --read-model \"OrderSummary\" --xml '<field name=\"customer\" type=\"Custom\">\n <field name=\"customerId\" type=\"UUID\"/>\n <field name=\"name\" type=\"String\"/>\n <field name=\"billingAddress\" type=\"Custom\">\n <field name=\"street\" type=\"String\"/>\n <field name=\"city\" type=\"String\"/>\n <field name=\"country\" type=\"String\"/>\n </field>\n</field>'\n```\n\n---\n\n## Mapping Fields on Flows\n\nField mappings define how data flows from source elements to target elements. When you connect an Event to a ReadModel with a flow, the field mappings specify which event fields populate which read model fields.\n\n### Command Syntax\n\n```bash\neventmodeler map fields --flow \"<Source>\u2192<Target>\" --xml '<mapping from=\"sourceField\" to=\"targetField\"/>'\n```\n\nOr with multiple mappings:\n\n```bash\neventmodeler map fields --flow \"<Source>\u2192<Target>\" --xml '\n <mapping from=\"orderId\" to=\"orderId\"/>\n <mapping from=\"customerId\" to=\"customerId\"/>\n <mapping from=\"totalAmount\" to=\"total\"/>\n'\n```\n\n### Flow Identifier Formats\n\nThe `--flow` argument accepts:\n\n1. **Name arrow format** (most common): `\"SourceName\u2192TargetName\"` or `\"SourceName->TargetName\"`\n2. **Element ID prefix** (for duplicates): `\"id:abc12345\u2192TargetName\"`\n3. **Flow ID directly**: The UUID of the flow itself\n\nNote: The `id:` prefix works with every command, not just `map fields`. See `eventmodeler guide explore` for details.\n\n### JSON Alternative\n\n```bash\neventmodeler map fields --flow \"OrderPlaced\u2192OrderSummary\" --json '[\n {\"from\": \"orderId\", \"to\": \"orderId\"},\n {\"from\": \"customerId\", \"to\": \"customerId\"}\n]'\n```\n\n### Workflow\n\n1. **Find flows needing mappings**: `eventmodeler show model-completeness`\n2. **See source fields**: `eventmodeler show event \"OrderPlaced\"`\n3. **See target fields**: `eventmodeler show completeness \"OrderSummary\"`\n4. **Create mappings**: `eventmodeler map fields --flow \"OrderPlaced\u2192OrderSummary\" --xml '...'`\n5. **Verify**: `eventmodeler show completeness \"OrderSummary\"`\n\n### Mapping Nested Fields\n\nFor Custom (nested) field types, use dot notation:\n\n```bash\neventmodeler map fields --flow \"OrderPlaced\u2192OrderSummary\" --xml '\n <mapping from=\"shippingAddress.street\" to=\"deliveryAddress.street\"/>\n <mapping from=\"shippingAddress.city\" to=\"deliveryAddress.city\"/>\n <mapping from=\"shippingAddress.postalCode\" to=\"deliveryAddress.zip\"/>\n'\n```\n\n### Multiple Flows to Same Target\n\nA read model often receives data from multiple events. Map each flow separately:\n\n```bash\n# Initial data from OrderPlaced\neventmodeler map fields --flow \"OrderPlaced\u2192OrderSummary\" --xml '\n <mapping from=\"orderId\" to=\"orderId\"/>\n <mapping from=\"customerId\" to=\"customerId\"/>\n <mapping from=\"totalAmount\" to=\"total\"/>\n'\n\n# Status updates from OrderShipped\neventmodeler map fields --flow \"OrderShipped\u2192OrderSummary\" --xml '\n <mapping from=\"shippedAt\" to=\"shippedDate\"/>\n <mapping from=\"trackingNumber\" to=\"trackingNumber\"/>\n'\n```\n\n### Updating Existing Mappings\n\nRunning `map fields` again **merges** with existing mappings:\n- New mappings are added\n- If a target field already has a mapping, it's replaced with the new source\n\n### Common Mapping Patterns\n\n| Pattern | Example |\n|---------|---------|\n| Identity | `<mapping from=\"orderId\" to=\"orderId\"/>` |\n| Rename | `<mapping from=\"totalAmount\" to=\"total\"/>` |\n| Nested to flat | `<mapping from=\"customer.id\" to=\"customerId\"/>` |\n| Flat to nested | `<mapping from=\"street\" to=\"address.street\"/>` |\n\n---\n\n## Updating Field Properties\n\nUse `update field` to modify field attributes without changing the field's name or structure.\n\n### Command Syntax\n\n```bash\neventmodeler update field --<element-type> \"<name>\" --field \"<field-name>\" [options]\n```\n\nElement types: `--command`, `--event`, `--read-model`, `--screen`, `--processor`\n\n### Available Options\n\n| Option | Values | Description |\n|--------|--------|-------------|\n| `--optional` | `true`/`false` | Mark field as not required for completeness |\n| `--generated` | `true`/`false` | Mark field as system-generated (not from user input) |\n| `--user-input` | `true`/`false` | Mark field as user-provided (screens only) |\n| `--type` | Field type | Change the field's data type |\n\n### Examples\n\n```bash\n# Mark a field as optional\neventmodeler update field --event \"OrderPlaced\" --field \"promoCode\" --optional true\n\n# Mark a field as system-generated\neventmodeler update field --event \"OrderPlaced\" --field \"orderId\" --generated true\n\n# Mark screen fields as user input\neventmodeler update field --screen \"Checkout\" --field \"email\" --user-input true\n\n# Change a field's type\neventmodeler update field --read-model \"OrderSummary\" --field \"total\" --type Decimal\n\n# Combine multiple updates\neventmodeler update field --event \"UserRegistered\" --field \"createdAt\" --generated true --optional false\n```\n\n### Updating Nested Fields\n\nUse dot notation for fields inside Custom types:\n\n```bash\neventmodeler update field --event \"OrderPlaced\" --field \"shippingAddress.postalCode\" --optional true\n```\n\n### Field Property Meanings\n\n**isOptional**: Field may not always have a value. Completeness checks don't require this field to have a mapping source.\n\n**isGenerated**: Field is system-generated, not derived from other fields. Completeness checks don't require this field to have a mapping source. Use for: IDs, timestamps, sequence numbers, computed values.\n\n**isUserInput** (screens only): Field comes from user input on the screen. Identifies which screen fields are inputs vs. display-only.\n\n---\n\n## Removing Fields\n\n```bash\n# Remove a field from an event\neventmodeler remove field --event \"OrderPlaced\" --field \"legacyId\"\n\n# Remove a field from a read model\neventmodeler remove field --read-model \"OrderSummary\" --field \"deprecatedStatus\"\n\n# Remove a nested field using dot notation\neventmodeler remove field --event \"OrderPlaced\" --field \"metadata.debugInfo\"\n```\n\n**Caution**: Removing a field may break existing field mappings. Check completeness first:\n```bash\neventmodeler show completeness \"<element-name>\"\n```\n\n---\n\n## Checking Completeness\n\n### Check a Specific Element\n```bash\neventmodeler show completeness \"<name>\"\n```\nShows incoming flows and which fields are satisfied vs unsatisfied.\n\n### Check the Entire Model\n```bash\neventmodeler show model-completeness\n```\nProject-wide view: complete count, incomplete count, and all incomplete flows with their unsatisfied fields.\n\n### Check Aggregate ID Fields\n```bash\neventmodeler show aggregate-completeness \"<aggregate-name>\"\n```\n\n---\n\n## Common Mistakes\n\n- **Creating empty Custom fields**: Custom fields MUST have subfields.\n - Wrong: `<field name=\"address\" type=\"Custom\"/>`\n - Right: `<field name=\"address\" type=\"Custom\"><field name=\"street\" type=\"String\"/>...</field>`\n\n- **Using \"List\" as a field type**: Use `isList=\"true\"` with a valid type instead.\n - Wrong: `<field name=\"items\" type=\"List\"/>`\n - Right: `<field name=\"items\" type=\"String\" isList=\"true\"/>`\n\n- **Using wrong attribute names**: Always use camelCase attributes.\n - Wrong: `optional=\"true\"`, `generated=\"true\"`, `list=\"true\"`\n - Right: `isOptional=\"true\"`, `isGenerated=\"true\"`, `isList=\"true\"`\n\n## Best Practices\n\n1. **Get approval before adding fields** - Propose the design, let the user confirm\n2. **Consider all source events** - A read model may need data from multiple events\n3. **Use appropriate types** - `Decimal` for money, `DateTime` for timestamps\n4. **Mark optional fields** - Fields that may not always have values (e.g., `cancelledAt`)\n5. **Mark generated fields early** - When creating slices, immediately mark IDs and timestamps as generated\n6. **Map all fields at once** - More efficient than one mapping at a time\n7. **Verify after mapping** - Run `show completeness` to confirm all fields are satisfied\n8. **Check mappings before removing** - Removing a field can break existing mappings\n";
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'information-flow',
|
|
3
|
+
description: 'Design fields, map data between elements, update properties, and check completeness',
|
|
4
|
+
};
|
|
5
|
+
export const content = `
|
|
6
|
+
# Information Flow: Fields, Mappings, and Completeness
|
|
7
|
+
|
|
8
|
+
This guide covers the full lifecycle of information in an event model: designing fields, adding them to elements, mapping data between connected elements, updating field properties, and verifying completeness.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Designing Read Models
|
|
13
|
+
|
|
14
|
+
Read models are projections of event data - views that serve specific screens or use cases. Every field in a read model should trace back to event fields.
|
|
15
|
+
|
|
16
|
+
### Understand the Context
|
|
17
|
+
\`\`\`bash
|
|
18
|
+
# Look at the slice containing the read model
|
|
19
|
+
eventmodeler show slice "<slice-name>"
|
|
20
|
+
|
|
21
|
+
# Check current completeness - what's missing?
|
|
22
|
+
eventmodeler show completeness "<read-model-name>"
|
|
23
|
+
|
|
24
|
+
# See what events feed into this read model
|
|
25
|
+
eventmodeler list events
|
|
26
|
+
\`\`\`
|
|
27
|
+
|
|
28
|
+
### Design the Fields
|
|
29
|
+
Based on the use case, identify what fields are needed:
|
|
30
|
+
- **Identity fields** - IDs to identify the entity
|
|
31
|
+
- **Display fields** - What the user sees
|
|
32
|
+
- **Status fields** - Current state
|
|
33
|
+
- **Computed fields** - Aggregations, counts, derived values
|
|
34
|
+
- **Timestamp fields** - When things happened
|
|
35
|
+
|
|
36
|
+
### Propose Before Adding
|
|
37
|
+
\`\`\`
|
|
38
|
+
I suggest these fields for the OrderHistory read model:
|
|
39
|
+
- orderId (UUID) - identifies the order
|
|
40
|
+
- customerId (UUID) - who placed it
|
|
41
|
+
- status (String) - current status: pending, shipped, delivered, cancelled
|
|
42
|
+
- total (Decimal) - order total
|
|
43
|
+
- placedAt (DateTime) - when it was placed
|
|
44
|
+
- lastUpdatedAt (DateTime) - when status last changed
|
|
45
|
+
|
|
46
|
+
Does this look right?
|
|
47
|
+
\`\`\`
|
|
48
|
+
|
|
49
|
+
## Adding Fields
|
|
50
|
+
|
|
51
|
+
Field types: \`UUID\`, \`String\`, \`Int\`, \`Long\`, \`Double\`, \`Decimal\`, \`Boolean\`, \`Date\`, \`DateTime\`, \`Custom\`
|
|
52
|
+
|
|
53
|
+
\`\`\`bash
|
|
54
|
+
# Add a simple field
|
|
55
|
+
eventmodeler add field --read-model "OrderSummary" --xml '<field name="orderId" type="UUID"/>'
|
|
56
|
+
|
|
57
|
+
# Add an optional field
|
|
58
|
+
eventmodeler add field --read-model "OrderSummary" --xml '<field name="cancelledAt" type="DateTime" isOptional="true"/>'
|
|
59
|
+
|
|
60
|
+
# Add a list field
|
|
61
|
+
eventmodeler add field --read-model "OrderSummary" --xml '<field name="itemIds" type="UUID" isList="true"/>'
|
|
62
|
+
|
|
63
|
+
# Add a complex/nested field
|
|
64
|
+
eventmodeler add field --read-model "OrderSummary" --xml '<field name="shippingAddress" type="Custom">
|
|
65
|
+
<field name="street" type="String"/>
|
|
66
|
+
<field name="city" type="String"/>
|
|
67
|
+
<field name="postalCode" type="String"/>
|
|
68
|
+
</field>'
|
|
69
|
+
\`\`\`
|
|
70
|
+
|
|
71
|
+
**CRITICAL: Custom fields MUST contain subfields.** A Custom field without subfields is invalid.
|
|
72
|
+
|
|
73
|
+
### Lists and Custom Types
|
|
74
|
+
|
|
75
|
+
**List of custom objects** - combine \`type="Custom"\` with \`isList="true"\`:
|
|
76
|
+
\`\`\`bash
|
|
77
|
+
eventmodeler add field --read-model "OrderSummary" --xml '<field name="lineItems" type="Custom" isList="true">
|
|
78
|
+
<field name="productId" type="UUID"/>
|
|
79
|
+
<field name="productName" type="String"/>
|
|
80
|
+
<field name="quantity" type="Int"/>
|
|
81
|
+
<field name="unitPrice" type="Decimal"/>
|
|
82
|
+
</field>'
|
|
83
|
+
\`\`\`
|
|
84
|
+
|
|
85
|
+
**Nested custom types** - subfields can themselves be Custom:
|
|
86
|
+
\`\`\`bash
|
|
87
|
+
eventmodeler add field --read-model "OrderSummary" --xml '<field name="customer" type="Custom">
|
|
88
|
+
<field name="customerId" type="UUID"/>
|
|
89
|
+
<field name="name" type="String"/>
|
|
90
|
+
<field name="billingAddress" type="Custom">
|
|
91
|
+
<field name="street" type="String"/>
|
|
92
|
+
<field name="city" type="String"/>
|
|
93
|
+
<field name="country" type="String"/>
|
|
94
|
+
</field>
|
|
95
|
+
</field>'
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Mapping Fields on Flows
|
|
101
|
+
|
|
102
|
+
Field mappings define how data flows from source elements to target elements. When you connect an Event to a ReadModel with a flow, the field mappings specify which event fields populate which read model fields.
|
|
103
|
+
|
|
104
|
+
### Command Syntax
|
|
105
|
+
|
|
106
|
+
\`\`\`bash
|
|
107
|
+
eventmodeler map fields --flow "<Source>→<Target>" --xml '<mapping from="sourceField" to="targetField"/>'
|
|
108
|
+
\`\`\`
|
|
109
|
+
|
|
110
|
+
Or with multiple mappings:
|
|
111
|
+
|
|
112
|
+
\`\`\`bash
|
|
113
|
+
eventmodeler map fields --flow "<Source>→<Target>" --xml '
|
|
114
|
+
<mapping from="orderId" to="orderId"/>
|
|
115
|
+
<mapping from="customerId" to="customerId"/>
|
|
116
|
+
<mapping from="totalAmount" to="total"/>
|
|
117
|
+
'
|
|
118
|
+
\`\`\`
|
|
119
|
+
|
|
120
|
+
### Flow Identifier Formats
|
|
121
|
+
|
|
122
|
+
The \`--flow\` argument accepts:
|
|
123
|
+
|
|
124
|
+
1. **Name arrow format** (most common): \`"SourceName→TargetName"\` or \`"SourceName->TargetName"\`
|
|
125
|
+
2. **Element ID prefix** (for duplicates): \`"id:abc12345→TargetName"\`
|
|
126
|
+
3. **Flow ID directly**: The UUID of the flow itself
|
|
127
|
+
|
|
128
|
+
Note: The \`id:\` prefix works with every command, not just \`map fields\`. See \`eventmodeler guide explore\` for details.
|
|
129
|
+
|
|
130
|
+
### JSON Alternative
|
|
131
|
+
|
|
132
|
+
\`\`\`bash
|
|
133
|
+
eventmodeler map fields --flow "OrderPlaced→OrderSummary" --json '[
|
|
134
|
+
{"from": "orderId", "to": "orderId"},
|
|
135
|
+
{"from": "customerId", "to": "customerId"}
|
|
136
|
+
]'
|
|
137
|
+
\`\`\`
|
|
138
|
+
|
|
139
|
+
### Workflow
|
|
140
|
+
|
|
141
|
+
1. **Find flows needing mappings**: \`eventmodeler show model-completeness\`
|
|
142
|
+
2. **See source fields**: \`eventmodeler show event "OrderPlaced"\`
|
|
143
|
+
3. **See target fields**: \`eventmodeler show completeness "OrderSummary"\`
|
|
144
|
+
4. **Create mappings**: \`eventmodeler map fields --flow "OrderPlaced→OrderSummary" --xml '...'\`
|
|
145
|
+
5. **Verify**: \`eventmodeler show completeness "OrderSummary"\`
|
|
146
|
+
|
|
147
|
+
### Mapping Nested Fields
|
|
148
|
+
|
|
149
|
+
For Custom (nested) field types, use dot notation:
|
|
150
|
+
|
|
151
|
+
\`\`\`bash
|
|
152
|
+
eventmodeler map fields --flow "OrderPlaced→OrderSummary" --xml '
|
|
153
|
+
<mapping from="shippingAddress.street" to="deliveryAddress.street"/>
|
|
154
|
+
<mapping from="shippingAddress.city" to="deliveryAddress.city"/>
|
|
155
|
+
<mapping from="shippingAddress.postalCode" to="deliveryAddress.zip"/>
|
|
156
|
+
'
|
|
157
|
+
\`\`\`
|
|
158
|
+
|
|
159
|
+
### Multiple Flows to Same Target
|
|
160
|
+
|
|
161
|
+
A read model often receives data from multiple events. Map each flow separately:
|
|
162
|
+
|
|
163
|
+
\`\`\`bash
|
|
164
|
+
# Initial data from OrderPlaced
|
|
165
|
+
eventmodeler map fields --flow "OrderPlaced→OrderSummary" --xml '
|
|
166
|
+
<mapping from="orderId" to="orderId"/>
|
|
167
|
+
<mapping from="customerId" to="customerId"/>
|
|
168
|
+
<mapping from="totalAmount" to="total"/>
|
|
169
|
+
'
|
|
170
|
+
|
|
171
|
+
# Status updates from OrderShipped
|
|
172
|
+
eventmodeler map fields --flow "OrderShipped→OrderSummary" --xml '
|
|
173
|
+
<mapping from="shippedAt" to="shippedDate"/>
|
|
174
|
+
<mapping from="trackingNumber" to="trackingNumber"/>
|
|
175
|
+
'
|
|
176
|
+
\`\`\`
|
|
177
|
+
|
|
178
|
+
### Updating Existing Mappings
|
|
179
|
+
|
|
180
|
+
Running \`map fields\` again **merges** with existing mappings:
|
|
181
|
+
- New mappings are added
|
|
182
|
+
- If a target field already has a mapping, it's replaced with the new source
|
|
183
|
+
|
|
184
|
+
### Common Mapping Patterns
|
|
185
|
+
|
|
186
|
+
| Pattern | Example |
|
|
187
|
+
|---------|---------|
|
|
188
|
+
| Identity | \`<mapping from="orderId" to="orderId"/>\` |
|
|
189
|
+
| Rename | \`<mapping from="totalAmount" to="total"/>\` |
|
|
190
|
+
| Nested to flat | \`<mapping from="customer.id" to="customerId"/>\` |
|
|
191
|
+
| Flat to nested | \`<mapping from="street" to="address.street"/>\` |
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Updating Field Properties
|
|
196
|
+
|
|
197
|
+
Use \`update field\` to modify field attributes without changing the field's name or structure.
|
|
198
|
+
|
|
199
|
+
### Command Syntax
|
|
200
|
+
|
|
201
|
+
\`\`\`bash
|
|
202
|
+
eventmodeler update field --<element-type> "<name>" --field "<field-name>" [options]
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
Element types: \`--command\`, \`--event\`, \`--read-model\`, \`--screen\`, \`--processor\`
|
|
206
|
+
|
|
207
|
+
### Available Options
|
|
208
|
+
|
|
209
|
+
| Option | Values | Description |
|
|
210
|
+
|--------|--------|-------------|
|
|
211
|
+
| \`--optional\` | \`true\`/\`false\` | Mark field as not required for completeness |
|
|
212
|
+
| \`--generated\` | \`true\`/\`false\` | Mark field as system-generated (not from user input) |
|
|
213
|
+
| \`--user-input\` | \`true\`/\`false\` | Mark field as user-provided (screens only) |
|
|
214
|
+
| \`--type\` | Field type | Change the field's data type |
|
|
215
|
+
|
|
216
|
+
### Examples
|
|
217
|
+
|
|
218
|
+
\`\`\`bash
|
|
219
|
+
# Mark a field as optional
|
|
220
|
+
eventmodeler update field --event "OrderPlaced" --field "promoCode" --optional true
|
|
221
|
+
|
|
222
|
+
# Mark a field as system-generated
|
|
223
|
+
eventmodeler update field --event "OrderPlaced" --field "orderId" --generated true
|
|
224
|
+
|
|
225
|
+
# Mark screen fields as user input
|
|
226
|
+
eventmodeler update field --screen "Checkout" --field "email" --user-input true
|
|
227
|
+
|
|
228
|
+
# Change a field's type
|
|
229
|
+
eventmodeler update field --read-model "OrderSummary" --field "total" --type Decimal
|
|
230
|
+
|
|
231
|
+
# Combine multiple updates
|
|
232
|
+
eventmodeler update field --event "UserRegistered" --field "createdAt" --generated true --optional false
|
|
233
|
+
\`\`\`
|
|
234
|
+
|
|
235
|
+
### Updating Nested Fields
|
|
236
|
+
|
|
237
|
+
Use dot notation for fields inside Custom types:
|
|
238
|
+
|
|
239
|
+
\`\`\`bash
|
|
240
|
+
eventmodeler update field --event "OrderPlaced" --field "shippingAddress.postalCode" --optional true
|
|
241
|
+
\`\`\`
|
|
242
|
+
|
|
243
|
+
### Field Property Meanings
|
|
244
|
+
|
|
245
|
+
**isOptional**: Field may not always have a value. Completeness checks don't require this field to have a mapping source.
|
|
246
|
+
|
|
247
|
+
**isGenerated**: Field is system-generated, not derived from other fields. Completeness checks don't require this field to have a mapping source. Use for: IDs, timestamps, sequence numbers, computed values.
|
|
248
|
+
|
|
249
|
+
**isUserInput** (screens only): Field comes from user input on the screen. Identifies which screen fields are inputs vs. display-only.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Removing Fields
|
|
254
|
+
|
|
255
|
+
\`\`\`bash
|
|
256
|
+
# Remove a field from an event
|
|
257
|
+
eventmodeler remove field --event "OrderPlaced" --field "legacyId"
|
|
258
|
+
|
|
259
|
+
# Remove a field from a read model
|
|
260
|
+
eventmodeler remove field --read-model "OrderSummary" --field "deprecatedStatus"
|
|
261
|
+
|
|
262
|
+
# Remove a nested field using dot notation
|
|
263
|
+
eventmodeler remove field --event "OrderPlaced" --field "metadata.debugInfo"
|
|
264
|
+
\`\`\`
|
|
265
|
+
|
|
266
|
+
**Caution**: Removing a field may break existing field mappings. Check completeness first:
|
|
267
|
+
\`\`\`bash
|
|
268
|
+
eventmodeler show completeness "<element-name>"
|
|
269
|
+
\`\`\`
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Checking Completeness
|
|
274
|
+
|
|
275
|
+
### Check a Specific Element
|
|
276
|
+
\`\`\`bash
|
|
277
|
+
eventmodeler show completeness "<name>"
|
|
278
|
+
\`\`\`
|
|
279
|
+
Shows incoming flows and which fields are satisfied vs unsatisfied.
|
|
280
|
+
|
|
281
|
+
### Check the Entire Model
|
|
282
|
+
\`\`\`bash
|
|
283
|
+
eventmodeler show model-completeness
|
|
284
|
+
\`\`\`
|
|
285
|
+
Project-wide view: complete count, incomplete count, and all incomplete flows with their unsatisfied fields.
|
|
286
|
+
|
|
287
|
+
### Check Aggregate ID Fields
|
|
288
|
+
\`\`\`bash
|
|
289
|
+
eventmodeler show aggregate-completeness "<aggregate-name>"
|
|
290
|
+
\`\`\`
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Common Mistakes
|
|
295
|
+
|
|
296
|
+
- **Creating empty Custom fields**: Custom fields MUST have subfields.
|
|
297
|
+
- Wrong: \`<field name="address" type="Custom"/>\`
|
|
298
|
+
- Right: \`<field name="address" type="Custom"><field name="street" type="String"/>...</field>\`
|
|
299
|
+
|
|
300
|
+
- **Using "List" as a field type**: Use \`isList="true"\` with a valid type instead.
|
|
301
|
+
- Wrong: \`<field name="items" type="List"/>\`
|
|
302
|
+
- Right: \`<field name="items" type="String" isList="true"/>\`
|
|
303
|
+
|
|
304
|
+
- **Using wrong attribute names**: Always use camelCase attributes.
|
|
305
|
+
- Wrong: \`optional="true"\`, \`generated="true"\`, \`list="true"\`
|
|
306
|
+
- Right: \`isOptional="true"\`, \`isGenerated="true"\`, \`isList="true"\`
|
|
307
|
+
|
|
308
|
+
## Best Practices
|
|
309
|
+
|
|
310
|
+
1. **Get approval before adding fields** - Propose the design, let the user confirm
|
|
311
|
+
2. **Consider all source events** - A read model may need data from multiple events
|
|
312
|
+
3. **Use appropriate types** - \`Decimal\` for money, \`DateTime\` for timestamps
|
|
313
|
+
4. **Mark optional fields** - Fields that may not always have values (e.g., \`cancelledAt\`)
|
|
314
|
+
5. **Mark generated fields early** - When creating slices, immediately mark IDs and timestamps as generated
|
|
315
|
+
6. **Map all fields at once** - More efficient than one mapping at a time
|
|
316
|
+
7. **Verify after mapping** - Run \`show completeness\` to confirm all fields are satisfied
|
|
317
|
+
8. **Check mappings before removing** - Removing a field can break existing mappings
|
|
318
|
+
`;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const meta: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const content = "\n# Designing Scenarios for Slices\n\nScenarios define the behavior of a slice. There are two main scenario patterns:\n\n1. **State-Change Scenarios** (for state-change slices): Given events \u2192 When command \u2192 Then events/error\n2. **Automation Scenarios** (for automation slices): Given events \u2192 Then command (simpler Given-Then format)\n\n## Your Workflow\n\nWhen helping design scenarios for a slice:\n\n### 1. Gather Context\n```bash\n# Look at the slice to understand its components\neventmodeler show slice \"<slice-name>\"\n\n# See what chapter it belongs to and nearby slices for context\neventmodeler list chapters\neventmodeler show chapter \"<chapter-name>\"\n\n# List all events to understand what's available for Given clauses\neventmodeler list events\n```\n\n### 2. Identify the Slice Type\n\n**State-Change Slice** (Screen \u2192 Command \u2192 Event):\n- User triggers a command\n- Command produces event(s)\n- Use: Given \u2192 When command \u2192 Then events/error\n\n**Automation Slice** (ReadModel \u2192 Processor \u2192 Command \u2192 Event):\n- Events trigger the processor to issue a command\n- Use simpler Given \u2192 Then format (no When)\n- Given: All events that lead to the trigger condition\n- Then: The command that should be dispatched (or noCommand)\n\n### 3. Design Scenarios by Type\n\n---\n\n## State-Change Scenarios\n\nFor slices where a user action triggers a command:\n\n**Happy Path** - The command succeeds under normal conditions\n- Given: Any required preconditions (existing events)\n- When: The command with valid input\n- Then: The expected event(s) with field values\n\n**Validation Errors** - Bad input is rejected\n- Given: (may be empty)\n- When: Command with invalid input\n- Then: Error with message\n\n**Business Rule Violations** - Valid input but rule prevents action\n- Given: Events that create a state where the action isn't allowed\n- When: The command\n- Then: Error explaining why\n\n### State-Change Examples\n\n```bash\n# Happy path - command succeeds\neventmodeler add scenario --slice \"<slice-name>\" --xml '<scenario name=\"Successfully place order\" description=\"A registered customer can place an order with valid items\">\n <given>\n <event name=\"CustomerRegistered\">\n <field name=\"customerId\">cust-123</field>\n </event>\n </given>\n <when>\n <command name=\"PlaceOrder\">\n <field name=\"customerId\">cust-123</field>\n <field name=\"items\">[{\"productId\": \"prod-1\", \"quantity\": 2}]</field>\n </command>\n </when>\n <then type=\"events\">\n <event name=\"OrderPlaced\">\n <field name=\"orderId\">order-456</field>\n <field name=\"customerId\">cust-123</field>\n </event>\n </then>\n</scenario>'\n```\n\n```bash\n# Validation error\neventmodeler add scenario --slice \"<slice-name>\" --xml '<scenario name=\"Cannot place empty order\" description=\"Orders must contain at least one item - empty item lists are rejected\">\n <when>\n <command name=\"PlaceOrder\">\n <field name=\"customerId\">cust-123</field>\n <field name=\"items\">[]</field>\n </command>\n </when>\n <then type=\"error\" errorType=\"ValidationError\">Order must contain at least one item</then>\n</scenario>'\n```\n\n```bash\n# Business rule violation\neventmodeler add scenario --slice \"Cancel Order\" --xml '<scenario name=\"Cannot cancel shipped order\" description=\"Once an order has been shipped, it can no longer be cancelled\">\n <given>\n <event name=\"OrderPlaced\">\n <field name=\"orderId\">order-123</field>\n </event>\n <event name=\"OrderShipped\">\n <field name=\"orderId\">order-123</field>\n <field name=\"shippedAt\">2024-01-15T10:00:00Z</field>\n </event>\n </given>\n <when>\n <command name=\"CancelOrder\">\n <field name=\"orderId\">order-123</field>\n </command>\n </when>\n <then type=\"error\" errorType=\"BusinessRuleViolation\">Cannot cancel an order that has already shipped</then>\n</scenario>'\n```\n\n---\n\n## Automation Scenarios\n\nFor slices where events trigger a processor to issue a command. Use the simpler **Given-Then** format:\n\n**Trigger Conditions Met** - Events cause processor to act\n- Given: All the events that have occurred (including the trigger events)\n- Then: The command the processor issues\n\n**Trigger Conditions Not Met** - Events don't cause action\n- Given: Events that don't meet the trigger condition\n- Then: noCommand (processor should not dispatch)\n\n### Automation Examples\n\n```bash\n# Payment received triggers shipment initiation\n# Given: OrderPlaced + PaymentReceived \u2192 Then: InitiateShipment command\neventmodeler add scenario --slice \"Auto Ship Order\" --xml '<scenario name=\"Payment triggers shipment\" description=\"Given an order has been placed and payment received, the system automatically initiates shipment\">\n <given>\n <event name=\"OrderPlaced\">\n <field name=\"orderId\">order-123</field>\n <field name=\"customerId\">cust-456</field>\n </event>\n <event name=\"PaymentReceived\">\n <field name=\"orderId\">order-123</field>\n <field name=\"amount\">99.99</field>\n </event>\n </given>\n <then type=\"command\">\n <command name=\"InitiateShipment\">\n <field name=\"orderId\">order-123</field>\n <field name=\"warehouseId\">wh-001</field>\n </command>\n </then>\n</scenario>'\n```\n\n```bash\n# Negative case - conditions not met, no command dispatched\neventmodeler add scenario --slice \"Auto Ship Order\" --xml '<scenario name=\"No shipment without payment\" description=\"Given only an order placed (no payment), the system should not initiate shipment\">\n <given>\n <event name=\"OrderPlaced\">\n <field name=\"orderId\">order-123</field>\n <field name=\"customerId\">cust-456</field>\n </event>\n </given>\n <then type=\"noCommand\"/>\n</scenario>'\n```\n\n---\n\n## Read Model Assertions\n\nFor state-view slices, test that events project correctly to read models:\n\n```bash\neventmodeler add scenario --slice \"<slice-name>\" --xml '<scenario name=\"Order appears in summary\" description=\"After placing an order, it should be visible in the order summary with pending status\">\n <then type=\"readModelAssertion\">\n <read-model name=\"OrderSummary\">\n <field name=\"orderId\">order-456</field>\n <field name=\"status\">pending</field>\n </read-model>\n </then>\n</scenario>'\n```\n\n---\n\n## Scenario Types Summary\n\n| Slice Type | Format | Then Contains |\n|------------|--------|---------------|\n| State-Change | Given \u2192 When (Command) \u2192 Then | Events or Error |\n| Automation | Given (Events) \u2192 Then | Command or noCommand |\n| State-View | Given \u2192 Then | Read Model Assertion |\n\n## JSON Format Alternative\n\nYou can also use JSON format:\n\n**State-Change Scenario:**\n```bash\neventmodeler add scenario --slice \"<slice-name>\" --json '{\n \"name\": \"Successfully place order\",\n \"description\": \"A registered customer can place an order\",\n \"given\": [\n { \"event\": \"CustomerRegistered\", \"fieldValues\": { \"customerId\": \"cust-123\" } }\n ],\n \"when\": {\n \"command\": \"PlaceOrder\",\n \"commandFieldValues\": { \"customerId\": \"cust-123\", \"items\": [] }\n },\n \"then\": {\n \"type\": \"events\",\n \"events\": [\n { \"event\": \"OrderPlaced\", \"fieldValues\": { \"orderId\": \"order-456\" } }\n ]\n }\n}'\n```\n\n**Automation Scenario (Given-Then format):**\n```bash\neventmodeler add scenario --slice \"<slice-name>\" --json '{\n \"name\": \"Payment triggers shipment\",\n \"description\": \"Given order placed and payment received, shipment is initiated\",\n \"given\": [\n { \"event\": \"OrderPlaced\", \"fieldValues\": { \"orderId\": \"order-123\" } },\n { \"event\": \"PaymentReceived\", \"fieldValues\": { \"orderId\": \"order-123\", \"amount\": 99.99 } }\n ],\n \"then\": {\n \"type\": \"command\",\n \"command\": \"InitiateShipment\",\n \"commandFieldValues\": { \"orderId\": \"order-123\" }\n }\n}'\n```\n\n**Automation Scenario (negative case):**\n```bash\neventmodeler add scenario --slice \"<slice-name>\" --json '{\n \"name\": \"No shipment without payment\",\n \"description\": \"Given only order placed (no payment), no command dispatched\",\n \"given\": [\n { \"event\": \"OrderPlaced\", \"fieldValues\": { \"orderId\": \"order-123\" } }\n ],\n \"then\": {\n \"type\": \"noCommand\"\n }\n}'\n```\n\n## Best Practices\n\n1. **Write clear descriptions** - Explain why this scenario matters and what business rule it captures\n2. **Use realistic example values** - \"cust-123\" not \"string\", \"2024-01-15\" not \"date\"\n3. **Name scenarios descriptively** - \"Cannot cancel shipped order\" not \"Error case 1\"\n4. **Include all relevant fields** - Don't skip fields that matter for the scenario\n5. **Think about state** - What events need to exist for this scenario to make sense?\n6. **Cover the negative cases** - These often reveal missing business rules (use `noCommand` for automations)\n7. **Match format to slice type** - Use Given-When-Then for state-change, Given-Then for automation\n";
|