eventmodeler 0.6.0 → 0.6.2
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/index.js +7133 -34
- package/package.json +5 -4
- package/dist/api/client-config.js +0 -10
- package/dist/api/generated/client/client.gen.js +0 -235
- package/dist/api/generated/client/index.js +0 -6
- package/dist/api/generated/client/types.gen.js +0 -2
- package/dist/api/generated/client/utils.gen.js +0 -228
- package/dist/api/generated/client.gen.js +0 -4
- package/dist/api/generated/core/auth.gen.js +0 -14
- package/dist/api/generated/core/bodySerializer.gen.js +0 -57
- package/dist/api/generated/core/params.gen.js +0 -100
- package/dist/api/generated/core/pathSerializer.gen.js +0 -106
- package/dist/api/generated/core/queryKeySerializer.gen.js +0 -92
- package/dist/api/generated/core/serverSentEvents.gen.js +0 -133
- package/dist/api/generated/core/types.gen.js +0 -2
- package/dist/api/generated/core/utils.gen.js +0 -87
- package/dist/api/generated/index.js +0 -2
- package/dist/api/generated/sdk.gen.js +0 -4222
- package/dist/api/generated/types.gen.js +0 -2
- package/dist/api/generated/zod.gen.js +0 -7217
- package/dist/commands/add.js +0 -315
- package/dist/commands/auth.js +0 -14
- package/dist/commands/create.js +0 -192
- package/dist/commands/design.js +0 -108
- package/dist/commands/guide.js +0 -15
- package/dist/commands/init.js +0 -21
- package/dist/commands/list-schemas.js +0 -177
- package/dist/commands/list.js +0 -39
- package/dist/commands/loop.js +0 -101
- package/dist/commands/map.js +0 -40
- package/dist/commands/mark.js +0 -27
- package/dist/commands/move.js +0 -35
- package/dist/commands/remove.js +0 -170
- package/dist/commands/rename.js +0 -53
- package/dist/commands/resize.js +0 -30
- package/dist/commands/search.js +0 -14
- package/dist/commands/set.js +0 -199
- package/dist/commands/show-schemas.js +0 -259
- package/dist/commands/show.js +0 -56
- package/dist/commands/summary.js +0 -13
- package/dist/commands/update.js +0 -240
- package/dist/lib/auth.js +0 -331
- package/dist/lib/config.js +0 -80
- package/dist/lib/excalidraw-schema.js +0 -66
- package/dist/lib/globals.js +0 -8
- package/dist/lib/model.js +0 -11
- package/dist/lib/project-config.js +0 -103
- package/dist/lib/resolve.js +0 -59
- package/dist/lib/scenario.js +0 -15
- package/dist/slices/add-scenario/index.js +0 -103
- package/dist/slices/guide/guides/codegen.js +0 -339
- package/dist/slices/guide/guides/connect-slices.js +0 -202
- package/dist/slices/guide/guides/create-slices.js +0 -273
- package/dist/slices/guide/guides/explore.js +0 -238
- package/dist/slices/guide/guides/information-flow.js +0 -304
- package/dist/slices/guide/guides/scenarios.js +0 -214
- package/dist/slices/guide/index.js +0 -40
- package/dist/slices/help/index.js +0 -96
- package/dist/slices/help/topics/build-codegen.js +0 -109
- package/dist/slices/help/topics/build-slice.js +0 -147
- package/dist/slices/help/topics/check-completeness.js +0 -57
- package/dist/slices/help/topics/connect-slices.js +0 -99
- package/dist/slices/help/topics/explore-model.js +0 -112
- package/dist/slices/help/topics/json-reference.js +0 -188
- package/dist/slices/help/topics/linked-copies.js +0 -89
- package/dist/slices/help/topics/manipulate-canvas.js +0 -150
- package/dist/slices/help/topics/write-scenarios.js +0 -162
- package/dist/slices/init/index.js +0 -86
- package/dist/slices/init/loop.js +0 -60
- package/dist/slices/login/index.js +0 -20
- package/dist/slices/logout/index.js +0 -14
- package/dist/slices/open-app/index.js +0 -36
- package/dist/slices/whoami/index.js +0 -19
|
@@ -1,304 +0,0 @@
|
|
|
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 "OrderSummary" '{"name": "orderId", "type": "UUID"}'
|
|
56
|
-
|
|
57
|
-
# Add an optional field
|
|
58
|
-
eventmodeler add field "OrderSummary" '{"name": "cancelledAt", "type": "DateTime", "isOptional": true}'
|
|
59
|
-
|
|
60
|
-
# Add a list field
|
|
61
|
-
eventmodeler add field "OrderSummary" '{"name": "itemIds", "type": "UUID", "isList": true}'
|
|
62
|
-
|
|
63
|
-
# Add a complex/nested field
|
|
64
|
-
eventmodeler add field "OrderSummary" '{"name": "shippingAddress", "type": "Custom", "subfields": [
|
|
65
|
-
{"name": "street", "type": "String"},
|
|
66
|
-
{"name": "city", "type": "String"},
|
|
67
|
-
{"name": "postalCode", "type": "String"}
|
|
68
|
-
]}'
|
|
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 "OrderSummary" '{"name": "lineItems", "type": "Custom", "isList": true, "subfields": [
|
|
78
|
-
{"name": "productId", "type": "UUID"},
|
|
79
|
-
{"name": "productName", "type": "String"},
|
|
80
|
-
{"name": "quantity", "type": "Int"},
|
|
81
|
-
{"name": "unitPrice", "type": "Decimal"}
|
|
82
|
-
]}'
|
|
83
|
-
\`\`\`
|
|
84
|
-
|
|
85
|
-
**Nested custom types** - subfields can themselves be Custom:
|
|
86
|
-
\`\`\`bash
|
|
87
|
-
eventmodeler add field "OrderSummary" '{"name": "customer", "type": "Custom", "subfields": [
|
|
88
|
-
{"name": "customerId", "type": "UUID"},
|
|
89
|
-
{"name": "name", "type": "String"},
|
|
90
|
-
{"name": "billingAddress", "type": "Custom", "subfields": [
|
|
91
|
-
{"name": "street", "type": "String"},
|
|
92
|
-
{"name": "city", "type": "String"},
|
|
93
|
-
{"name": "country", "type": "String"}
|
|
94
|
-
]}
|
|
95
|
-
]}'
|
|
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 --from "<source>" --to "<target>" '[{"from": "sourceField", "to": "targetField"}]'
|
|
108
|
-
\`\`\`
|
|
109
|
-
|
|
110
|
-
Or with multiple mappings:
|
|
111
|
-
|
|
112
|
-
\`\`\`bash
|
|
113
|
-
eventmodeler map fields --from "OrderPlaced" --to "OrderSummary" '[
|
|
114
|
-
{"from": "orderId", "to": "orderId"},
|
|
115
|
-
{"from": "customerId", "to": "customerId"},
|
|
116
|
-
{"from": "totalAmount", "to": "total"}
|
|
117
|
-
]'
|
|
118
|
-
\`\`\`
|
|
119
|
-
|
|
120
|
-
### Source/Target Formats
|
|
121
|
-
|
|
122
|
-
The \`--from\` and \`--to\` arguments accept:
|
|
123
|
-
|
|
124
|
-
1. **Element name** (most common): \`"OrderPlaced"\`
|
|
125
|
-
2. **Element ID prefix** (for duplicates): \`"id:abc12345"\`
|
|
126
|
-
|
|
127
|
-
### Workflow
|
|
128
|
-
|
|
129
|
-
1. **Find flows needing mappings**: \`eventmodeler show model-completeness\`
|
|
130
|
-
2. **See source fields**: \`eventmodeler show event "OrderPlaced"\`
|
|
131
|
-
3. **See target fields**: \`eventmodeler show completeness "OrderSummary"\`
|
|
132
|
-
4. **Create mappings**: \`eventmodeler map fields --from "OrderPlaced" --to "OrderSummary" '[...]'\`
|
|
133
|
-
5. **Verify**: \`eventmodeler show completeness "OrderSummary"\`
|
|
134
|
-
|
|
135
|
-
### Mapping Nested Fields
|
|
136
|
-
|
|
137
|
-
For Custom (nested) field types, use dot notation:
|
|
138
|
-
|
|
139
|
-
\`\`\`bash
|
|
140
|
-
eventmodeler map fields --from "OrderPlaced" --to "OrderSummary" '[
|
|
141
|
-
{"from": "shippingAddress.street", "to": "deliveryAddress.street"},
|
|
142
|
-
{"from": "shippingAddress.city", "to": "deliveryAddress.city"},
|
|
143
|
-
{"from": "shippingAddress.postalCode", "to": "deliveryAddress.zip"}
|
|
144
|
-
]'
|
|
145
|
-
\`\`\`
|
|
146
|
-
|
|
147
|
-
### Multiple Flows to Same Target
|
|
148
|
-
|
|
149
|
-
A read model often receives data from multiple events. Map each flow separately:
|
|
150
|
-
|
|
151
|
-
\`\`\`bash
|
|
152
|
-
# Initial data from OrderPlaced
|
|
153
|
-
eventmodeler map fields --from "OrderPlaced" --to "OrderSummary" '[
|
|
154
|
-
{"from": "orderId", "to": "orderId"},
|
|
155
|
-
{"from": "customerId", "to": "customerId"},
|
|
156
|
-
{"from": "totalAmount", "to": "total"}
|
|
157
|
-
]'
|
|
158
|
-
|
|
159
|
-
# Status updates from OrderShipped
|
|
160
|
-
eventmodeler map fields --from "OrderShipped" --to "OrderSummary" '[
|
|
161
|
-
{"from": "shippedAt", "to": "shippedDate"},
|
|
162
|
-
{"from": "trackingNumber", "to": "trackingNumber"}
|
|
163
|
-
]'
|
|
164
|
-
\`\`\`
|
|
165
|
-
|
|
166
|
-
### Updating Existing Mappings
|
|
167
|
-
|
|
168
|
-
Running \`map fields\` again **merges** with existing mappings:
|
|
169
|
-
- New mappings are added
|
|
170
|
-
- If a target field already has a mapping, it's replaced with the new source
|
|
171
|
-
|
|
172
|
-
### Common Mapping Patterns
|
|
173
|
-
|
|
174
|
-
| Pattern | Example |
|
|
175
|
-
|---------|---------|
|
|
176
|
-
| Identity | \`{"from": "orderId", "to": "orderId"}\` |
|
|
177
|
-
| Rename | \`{"from": "totalAmount", "to": "total"}\` |
|
|
178
|
-
| Nested to flat | \`{"from": "customer.id", "to": "customerId"}\` |
|
|
179
|
-
| Flat to nested | \`{"from": "street", "to": "address.street"}\` |
|
|
180
|
-
|
|
181
|
-
---
|
|
182
|
-
|
|
183
|
-
## Updating Field Properties
|
|
184
|
-
|
|
185
|
-
Use \`update field\` to modify field attributes without changing the field's name or structure.
|
|
186
|
-
|
|
187
|
-
### Command Syntax
|
|
188
|
-
|
|
189
|
-
\`\`\`bash
|
|
190
|
-
eventmodeler update field <element-name> --field "<field-name>" [options]
|
|
191
|
-
\`\`\`
|
|
192
|
-
|
|
193
|
-
### Available Options
|
|
194
|
-
|
|
195
|
-
| Option | Values | Description |
|
|
196
|
-
|--------|--------|-------------|
|
|
197
|
-
| \`--optional\` | \`true\`/\`false\` | Mark field as not required for completeness |
|
|
198
|
-
| \`--generated\` | \`true\`/\`false\` | Mark field as system-generated (not from user input) |
|
|
199
|
-
| \`--user-input\` | \`true\`/\`false\` | Mark field as user-provided (screens only) |
|
|
200
|
-
| \`--type\` | Field type | Change the field's data type |
|
|
201
|
-
|
|
202
|
-
### Examples
|
|
203
|
-
|
|
204
|
-
\`\`\`bash
|
|
205
|
-
# Mark a field as optional
|
|
206
|
-
eventmodeler update field "OrderPlaced" --field "promoCode" --optional true
|
|
207
|
-
|
|
208
|
-
# Mark a field as system-generated
|
|
209
|
-
eventmodeler update field "OrderPlaced" --field "orderId" --generated true
|
|
210
|
-
|
|
211
|
-
# Mark screen fields as user input
|
|
212
|
-
eventmodeler update field "Checkout" --field "email" --user-input true
|
|
213
|
-
|
|
214
|
-
# Change a field's type
|
|
215
|
-
eventmodeler update field "OrderSummary" --field "total" --type Decimal
|
|
216
|
-
|
|
217
|
-
# Combine multiple updates
|
|
218
|
-
eventmodeler update field "UserRegistered" --field "createdAt" --generated true --optional false
|
|
219
|
-
\`\`\`
|
|
220
|
-
|
|
221
|
-
### Updating Nested Fields
|
|
222
|
-
|
|
223
|
-
Use dot notation for fields inside Custom types:
|
|
224
|
-
|
|
225
|
-
\`\`\`bash
|
|
226
|
-
eventmodeler update field "OrderPlaced" --field "shippingAddress.postalCode" --optional true
|
|
227
|
-
\`\`\`
|
|
228
|
-
|
|
229
|
-
### Field Property Meanings
|
|
230
|
-
|
|
231
|
-
**isOptional**: Field may not always have a value. Completeness checks don't require this field to have a mapping source.
|
|
232
|
-
|
|
233
|
-
**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.
|
|
234
|
-
|
|
235
|
-
**isUserInput** (screens only): Field comes from user input on the screen. Identifies which screen fields are inputs vs. display-only.
|
|
236
|
-
|
|
237
|
-
---
|
|
238
|
-
|
|
239
|
-
## Removing Fields
|
|
240
|
-
|
|
241
|
-
\`\`\`bash
|
|
242
|
-
# Remove a field from an event
|
|
243
|
-
eventmodeler remove field "OrderPlaced" --field "legacyId"
|
|
244
|
-
|
|
245
|
-
# Remove a field from a read model
|
|
246
|
-
eventmodeler remove field "OrderSummary" --field "deprecatedStatus"
|
|
247
|
-
|
|
248
|
-
# Remove a nested field using dot notation
|
|
249
|
-
eventmodeler remove field "OrderPlaced" --field "metadata.debugInfo"
|
|
250
|
-
\`\`\`
|
|
251
|
-
|
|
252
|
-
**Caution**: Removing a field may break existing field mappings. Check completeness first:
|
|
253
|
-
\`\`\`bash
|
|
254
|
-
eventmodeler show completeness "<element-name>"
|
|
255
|
-
\`\`\`
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## Checking Completeness
|
|
260
|
-
|
|
261
|
-
### Check a Specific Element
|
|
262
|
-
\`\`\`bash
|
|
263
|
-
eventmodeler show completeness "<name>"
|
|
264
|
-
\`\`\`
|
|
265
|
-
Shows incoming flows and which fields are satisfied vs unsatisfied.
|
|
266
|
-
|
|
267
|
-
### Check the Entire Model
|
|
268
|
-
\`\`\`bash
|
|
269
|
-
eventmodeler show model-completeness
|
|
270
|
-
\`\`\`
|
|
271
|
-
Project-wide view: complete count, incomplete count, and all incomplete flows with their unsatisfied fields.
|
|
272
|
-
|
|
273
|
-
### Check Aggregate ID Fields
|
|
274
|
-
\`\`\`bash
|
|
275
|
-
eventmodeler show aggregate-completeness "<aggregate-name>"
|
|
276
|
-
\`\`\`
|
|
277
|
-
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
## Common Mistakes
|
|
281
|
-
|
|
282
|
-
- **Creating empty Custom fields**: Custom fields MUST have subfields.
|
|
283
|
-
- Wrong: \`{"name": "address", "type": "Custom"}\`
|
|
284
|
-
- Right: \`{"name": "address", "type": "Custom", "subfields": [{"name": "street", "type": "String"}, ...]}\`
|
|
285
|
-
|
|
286
|
-
- **Using "List" as a field type**: Use \`"isList": true\` with a valid type instead.
|
|
287
|
-
- Wrong: \`{"name": "items", "type": "List"}\`
|
|
288
|
-
- Right: \`{"name": "items", "type": "String", "isList": true}\`
|
|
289
|
-
|
|
290
|
-
- **Using wrong attribute names**: Always use camelCase attributes.
|
|
291
|
-
- Wrong: \`"optional": true\`, \`"generated": true\`, \`"list": true\`
|
|
292
|
-
- Right: \`"isOptional": true\`, \`"isGenerated": true\`, \`"isList": true\`
|
|
293
|
-
|
|
294
|
-
## Best Practices
|
|
295
|
-
|
|
296
|
-
1. **Get approval before adding fields** - Propose the design, let the user confirm
|
|
297
|
-
2. **Consider all source events** - A read model may need data from multiple events
|
|
298
|
-
3. **Use appropriate types** - \`Decimal\` for money, \`DateTime\` for timestamps
|
|
299
|
-
4. **Mark optional fields** - Fields that may not always have values (e.g., \`cancelledAt\`)
|
|
300
|
-
5. **Mark generated fields early** - When creating slices, immediately mark IDs and timestamps as generated
|
|
301
|
-
6. **Map all fields at once** - More efficient than one mapping at a time
|
|
302
|
-
7. **Verify after mapping** - Run \`show completeness\` to confirm all fields are satisfied
|
|
303
|
-
8. **Check mappings before removing** - Removing a field can break existing mappings
|
|
304
|
-
`;
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
export const meta = {
|
|
2
|
-
name: 'scenarios',
|
|
3
|
-
description: 'Design Given-When-Then scenarios for slices: happy paths, errors, automations',
|
|
4
|
-
};
|
|
5
|
-
export const content = `
|
|
6
|
-
# Designing Scenarios for Slices
|
|
7
|
-
|
|
8
|
-
Scenarios define the behavior of a slice. There are two main scenario patterns:
|
|
9
|
-
|
|
10
|
-
1. **State-Change Scenarios** (for state-change slices): Given events → When command → Then events/error
|
|
11
|
-
2. **Automation Scenarios** (for automation slices): Given events → Then command (simpler Given-Then format)
|
|
12
|
-
|
|
13
|
-
## Your Workflow
|
|
14
|
-
|
|
15
|
-
When helping design scenarios for a slice:
|
|
16
|
-
|
|
17
|
-
### 1. Gather Context
|
|
18
|
-
\`\`\`bash
|
|
19
|
-
# Look at the slice to understand its components
|
|
20
|
-
eventmodeler show slice "<slice-name>"
|
|
21
|
-
|
|
22
|
-
# See what chapter it belongs to and nearby slices for context
|
|
23
|
-
eventmodeler list chapters
|
|
24
|
-
eventmodeler show chapter "<chapter-name>"
|
|
25
|
-
|
|
26
|
-
# List all events to understand what's available for Given clauses
|
|
27
|
-
eventmodeler list events
|
|
28
|
-
\`\`\`
|
|
29
|
-
|
|
30
|
-
### 2. Identify the Slice Type
|
|
31
|
-
|
|
32
|
-
**State-Change Slice** (Screen → Command → Event):
|
|
33
|
-
- User triggers a command
|
|
34
|
-
- Command produces event(s)
|
|
35
|
-
- Use: Given → When command → Then events/error
|
|
36
|
-
|
|
37
|
-
**Automation Slice** (ReadModel → Processor → Command → Event):
|
|
38
|
-
- Events trigger the processor to issue a command
|
|
39
|
-
- Use simpler Given → Then format (no When)
|
|
40
|
-
- Given: All events that lead to the trigger condition
|
|
41
|
-
- Then: The command that should be dispatched (or noCommand)
|
|
42
|
-
|
|
43
|
-
### 3. Design Scenarios by Type
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## State-Change Scenarios
|
|
48
|
-
|
|
49
|
-
For slices where a user action triggers a command:
|
|
50
|
-
|
|
51
|
-
**Happy Path** - The command succeeds under normal conditions
|
|
52
|
-
- Given: Any required preconditions (existing events)
|
|
53
|
-
- When: The command with valid input
|
|
54
|
-
- Then: The expected event(s) with field values
|
|
55
|
-
|
|
56
|
-
**Validation Errors** - Bad input is rejected
|
|
57
|
-
- Given: (may be empty)
|
|
58
|
-
- When: Command with invalid input
|
|
59
|
-
- Then: Error with message
|
|
60
|
-
|
|
61
|
-
**Business Rule Violations** - Valid input but rule prevents action
|
|
62
|
-
- Given: Events that create a state where the action isn't allowed
|
|
63
|
-
- When: The command
|
|
64
|
-
- Then: Error explaining why
|
|
65
|
-
|
|
66
|
-
### State-Change Examples
|
|
67
|
-
|
|
68
|
-
\`\`\`bash
|
|
69
|
-
# Happy path - command succeeds
|
|
70
|
-
eventmodeler add scenario --slice "<slice-name>" '{
|
|
71
|
-
"name": "Successfully place order",
|
|
72
|
-
"description": "A registered customer can place an order with valid items",
|
|
73
|
-
"given": [
|
|
74
|
-
{"event": "CustomerRegistered", "fieldValues": {"customerId": "cust-123"}}
|
|
75
|
-
],
|
|
76
|
-
"when": {
|
|
77
|
-
"command": "PlaceOrder",
|
|
78
|
-
"commandFieldValues": {"customerId": "cust-123", "items": [{"productId": "prod-1", "quantity": 2}]}
|
|
79
|
-
},
|
|
80
|
-
"then": {
|
|
81
|
-
"type": "events",
|
|
82
|
-
"events": [
|
|
83
|
-
{"event": "OrderPlaced", "fieldValues": {"orderId": "order-456", "customerId": "cust-123"}}
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
}'
|
|
87
|
-
\`\`\`
|
|
88
|
-
|
|
89
|
-
\`\`\`bash
|
|
90
|
-
# Validation error
|
|
91
|
-
eventmodeler add scenario --slice "<slice-name>" '{
|
|
92
|
-
"name": "Cannot place empty order",
|
|
93
|
-
"description": "Orders must contain at least one item - empty item lists are rejected",
|
|
94
|
-
"when": {
|
|
95
|
-
"command": "PlaceOrder",
|
|
96
|
-
"commandFieldValues": {"customerId": "cust-123", "items": []}
|
|
97
|
-
},
|
|
98
|
-
"then": {
|
|
99
|
-
"type": "error",
|
|
100
|
-
"errorType": "ValidationError",
|
|
101
|
-
"errorMessage": "Order must contain at least one item"
|
|
102
|
-
}
|
|
103
|
-
}'
|
|
104
|
-
\`\`\`
|
|
105
|
-
|
|
106
|
-
\`\`\`bash
|
|
107
|
-
# Business rule violation
|
|
108
|
-
eventmodeler add scenario --slice "Cancel Order" '{
|
|
109
|
-
"name": "Cannot cancel shipped order",
|
|
110
|
-
"description": "Once an order has been shipped, it can no longer be cancelled",
|
|
111
|
-
"given": [
|
|
112
|
-
{"event": "OrderPlaced", "fieldValues": {"orderId": "order-123"}},
|
|
113
|
-
{"event": "OrderShipped", "fieldValues": {"orderId": "order-123", "shippedAt": "2024-01-15T10:00:00Z"}}
|
|
114
|
-
],
|
|
115
|
-
"when": {
|
|
116
|
-
"command": "CancelOrder",
|
|
117
|
-
"commandFieldValues": {"orderId": "order-123"}
|
|
118
|
-
},
|
|
119
|
-
"then": {
|
|
120
|
-
"type": "error",
|
|
121
|
-
"errorType": "BusinessRuleViolation",
|
|
122
|
-
"errorMessage": "Cannot cancel an order that has already shipped"
|
|
123
|
-
}
|
|
124
|
-
}'
|
|
125
|
-
\`\`\`
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
## Automation Scenarios
|
|
130
|
-
|
|
131
|
-
For slices where events trigger a processor to issue a command. Use the simpler **Given-Then** format:
|
|
132
|
-
|
|
133
|
-
**Trigger Conditions Met** - Events cause processor to act
|
|
134
|
-
- Given: All the events that have occurred (including the trigger events)
|
|
135
|
-
- Then: The command the processor issues
|
|
136
|
-
|
|
137
|
-
**Trigger Conditions Not Met** - Events don't cause action
|
|
138
|
-
- Given: Events that don't meet the trigger condition
|
|
139
|
-
- Then: noCommand (processor should not dispatch)
|
|
140
|
-
|
|
141
|
-
### Automation Examples
|
|
142
|
-
|
|
143
|
-
\`\`\`bash
|
|
144
|
-
# Payment received triggers shipment initiation
|
|
145
|
-
eventmodeler add scenario --slice "Auto Ship Order" '{
|
|
146
|
-
"name": "Payment triggers shipment",
|
|
147
|
-
"description": "Given an order has been placed and payment received, the system automatically initiates shipment",
|
|
148
|
-
"given": [
|
|
149
|
-
{"event": "OrderPlaced", "fieldValues": {"orderId": "order-123", "customerId": "cust-456"}},
|
|
150
|
-
{"event": "PaymentReceived", "fieldValues": {"orderId": "order-123", "amount": 99.99}}
|
|
151
|
-
],
|
|
152
|
-
"then": {
|
|
153
|
-
"type": "command",
|
|
154
|
-
"command": "InitiateShipment",
|
|
155
|
-
"commandFieldValues": {"orderId": "order-123", "warehouseId": "wh-001"}
|
|
156
|
-
}
|
|
157
|
-
}'
|
|
158
|
-
\`\`\`
|
|
159
|
-
|
|
160
|
-
\`\`\`bash
|
|
161
|
-
# Negative case - conditions not met, no command dispatched
|
|
162
|
-
eventmodeler add scenario --slice "Auto Ship Order" '{
|
|
163
|
-
"name": "No shipment without payment",
|
|
164
|
-
"description": "Given only an order placed (no payment), the system should not initiate shipment",
|
|
165
|
-
"given": [
|
|
166
|
-
{"event": "OrderPlaced", "fieldValues": {"orderId": "order-123", "customerId": "cust-456"}}
|
|
167
|
-
],
|
|
168
|
-
"then": {
|
|
169
|
-
"type": "noCommand"
|
|
170
|
-
}
|
|
171
|
-
}'
|
|
172
|
-
\`\`\`
|
|
173
|
-
|
|
174
|
-
---
|
|
175
|
-
|
|
176
|
-
## Read Model Assertions
|
|
177
|
-
|
|
178
|
-
For state-view slices, test that events project correctly to read models:
|
|
179
|
-
|
|
180
|
-
\`\`\`bash
|
|
181
|
-
eventmodeler add scenario --slice "<slice-name>" '{
|
|
182
|
-
"name": "Order appears in summary",
|
|
183
|
-
"description": "After placing an order, it should be visible in the order summary with pending status",
|
|
184
|
-
"given": [
|
|
185
|
-
{"event": "OrderPlaced", "fieldValues": {"orderId": "order-456", "customerId": "cust-123", "status": "pending"}}
|
|
186
|
-
],
|
|
187
|
-
"then": {
|
|
188
|
-
"type": "readModelAssertion",
|
|
189
|
-
"readModel": "OrderSummary",
|
|
190
|
-
"expected": {"orderId": "order-456", "status": "pending"}
|
|
191
|
-
}
|
|
192
|
-
}'
|
|
193
|
-
\`\`\`
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
## Scenario Types Summary
|
|
198
|
-
|
|
199
|
-
| Slice Type | Format | Then Contains |
|
|
200
|
-
|------------|--------|---------------|
|
|
201
|
-
| State-Change | Given → When (Command) → Then | Events or Error |
|
|
202
|
-
| Automation | Given (Events) → Then | Command or noCommand |
|
|
203
|
-
| State-View | Given → Then | Read Model Assertion |
|
|
204
|
-
|
|
205
|
-
## Best Practices
|
|
206
|
-
|
|
207
|
-
1. **Write clear descriptions** - Explain why this scenario matters and what business rule it captures
|
|
208
|
-
2. **Use realistic example values** - "cust-123" not "string", "2024-01-15" not "date"
|
|
209
|
-
3. **Name scenarios descriptively** - "Cannot cancel shipped order" not "Error case 1"
|
|
210
|
-
4. **Include all relevant fields** - Don't skip fields that matter for the scenario
|
|
211
|
-
5. **Think about state** - What events need to exist for this scenario to make sense?
|
|
212
|
-
6. **Cover the negative cases** - These often reveal missing business rules (use \`noCommand\` for automations)
|
|
213
|
-
7. **Match format to slice type** - Use Given-When-Then for state-change, Given-Then for automation
|
|
214
|
-
`;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { meta as exploreMeta, content as exploreContent } from './guides/explore';
|
|
2
|
-
import { meta as scenariosMeta, content as scenariosContent } from './guides/scenarios';
|
|
3
|
-
import { meta as informationFlowMeta, content as informationFlowContent } from './guides/information-flow';
|
|
4
|
-
import { meta as createSlicesMeta, content as createSlicesContent } from './guides/create-slices';
|
|
5
|
-
import { meta as connectSlicesMeta, content as connectSlicesContent } from './guides/connect-slices';
|
|
6
|
-
import { meta as codegenMeta, content as codegenContent } from './guides/codegen';
|
|
7
|
-
const GUIDES = [
|
|
8
|
-
{ meta: exploreMeta, content: exploreContent },
|
|
9
|
-
{ meta: createSlicesMeta, content: createSlicesContent },
|
|
10
|
-
{ meta: connectSlicesMeta, content: connectSlicesContent },
|
|
11
|
-
{ meta: informationFlowMeta, content: informationFlowContent },
|
|
12
|
-
{ meta: scenariosMeta, content: scenariosContent },
|
|
13
|
-
{ meta: codegenMeta, content: codegenContent },
|
|
14
|
-
];
|
|
15
|
-
export function runGuide(subcommand) {
|
|
16
|
-
if (!subcommand) {
|
|
17
|
-
printGuideList();
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const guide = GUIDES.find(g => g.meta.name === subcommand);
|
|
21
|
-
if (!guide) {
|
|
22
|
-
console.error(`Unknown guide: ${subcommand}`);
|
|
23
|
-
console.error(`Run "eventmodeler guide" to see available guides.`);
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
console.log(guide.content);
|
|
27
|
-
}
|
|
28
|
-
function printGuideList() {
|
|
29
|
-
console.log(`
|
|
30
|
-
eventmodeler guide - Conceptual guides for working with event models
|
|
31
|
-
|
|
32
|
-
USAGE:
|
|
33
|
-
eventmodeler guide <name>
|
|
34
|
-
|
|
35
|
-
AVAILABLE GUIDES:
|
|
36
|
-
${GUIDES.map(g => ` ${g.meta.name.padEnd(20)} ${g.meta.description}`).join('\n')}
|
|
37
|
-
|
|
38
|
-
Run "eventmodeler guide <name>" to read a guide.
|
|
39
|
-
`);
|
|
40
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { meta as exploreModelMeta, content as exploreModelContent } from './topics/explore-model';
|
|
2
|
-
import { meta as buildSliceMeta, content as buildSliceContent } from './topics/build-slice';
|
|
3
|
-
import { meta as connectSlicesMeta, content as connectSlicesContent } from './topics/connect-slices';
|
|
4
|
-
import { meta as writeScenariosMeta, content as writeScenariosContent } from './topics/write-scenarios';
|
|
5
|
-
import { meta as manipulateCanvasMeta, content as manipulateCanvasContent } from './topics/manipulate-canvas';
|
|
6
|
-
import { meta as linkedCopiesMeta, content as linkedCopiesContent } from './topics/linked-copies';
|
|
7
|
-
import { meta as checkCompletenessMeta, content as checkCompletenessContent } from './topics/check-completeness';
|
|
8
|
-
import { meta as jsonReferenceMeta, content as jsonReferenceContent } from './topics/json-reference';
|
|
9
|
-
import { meta as buildCodegenMeta, content as buildCodegenContent } from './topics/build-codegen';
|
|
10
|
-
const TOPICS = [
|
|
11
|
-
{ meta: exploreModelMeta, content: exploreModelContent },
|
|
12
|
-
{ meta: buildSliceMeta, content: buildSliceContent },
|
|
13
|
-
{ meta: connectSlicesMeta, content: connectSlicesContent },
|
|
14
|
-
{ meta: writeScenariosMeta, content: writeScenariosContent },
|
|
15
|
-
{ meta: manipulateCanvasMeta, content: manipulateCanvasContent },
|
|
16
|
-
{ meta: linkedCopiesMeta, content: linkedCopiesContent },
|
|
17
|
-
{ meta: checkCompletenessMeta, content: checkCompletenessContent },
|
|
18
|
-
{ meta: jsonReferenceMeta, content: jsonReferenceContent },
|
|
19
|
-
{ meta: buildCodegenMeta, content: buildCodegenContent },
|
|
20
|
-
];
|
|
21
|
-
export function runHelp(query) {
|
|
22
|
-
if (!query) {
|
|
23
|
-
printTopicIndex();
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
// Exact name match
|
|
27
|
-
const exact = TOPICS.find(t => t.meta.name === query);
|
|
28
|
-
if (exact) {
|
|
29
|
-
console.log(exact.content);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
// Fuzzy keyword match
|
|
33
|
-
const matches = fuzzyMatch(query);
|
|
34
|
-
if (matches.length === 0) {
|
|
35
|
-
console.error(`No help topics matched: "${query}"`);
|
|
36
|
-
console.error(`Run "eventmodeler help" to see all topics.`);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
if (matches.length === 1) {
|
|
40
|
-
console.log(matches[0].topic.content);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
console.log(`\nMultiple topics matched "${query}":\n`);
|
|
44
|
-
for (const m of matches) {
|
|
45
|
-
console.log(` ${m.topic.meta.name.padEnd(24)} ${m.topic.meta.description} (${m.score} keyword hits)`);
|
|
46
|
-
}
|
|
47
|
-
console.log(`\nRun "eventmodeler help <topic-name>" to read one.\n`);
|
|
48
|
-
}
|
|
49
|
-
function fuzzyMatch(query) {
|
|
50
|
-
const words = query.toLowerCase().split(/\s+/).filter(w => w.length > 0);
|
|
51
|
-
if (words.length === 0)
|
|
52
|
-
return [];
|
|
53
|
-
const scored = [];
|
|
54
|
-
for (const topic of TOPICS) {
|
|
55
|
-
let score = 0;
|
|
56
|
-
const allKeywords = topic.meta.keywords.map(k => k.toLowerCase());
|
|
57
|
-
const nameWords = topic.meta.name.toLowerCase().split('-');
|
|
58
|
-
const descWords = topic.meta.description.toLowerCase().split(/\s+/);
|
|
59
|
-
const searchable = [...allKeywords, ...nameWords, ...descWords];
|
|
60
|
-
for (const word of words) {
|
|
61
|
-
for (const kw of searchable) {
|
|
62
|
-
if (kw === word) {
|
|
63
|
-
score += 2;
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
else if (kw.startsWith(word) || word.startsWith(kw)) {
|
|
67
|
-
score += 1;
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (score > 0) {
|
|
73
|
-
scored.push({ topic, score });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
scored.sort((a, b) => b.score - a.score);
|
|
77
|
-
return scored.slice(0, 5);
|
|
78
|
-
}
|
|
79
|
-
function printTopicIndex() {
|
|
80
|
-
console.log(`
|
|
81
|
-
eventmodeler help - Task-oriented help for the Event Modeler CLI
|
|
82
|
-
|
|
83
|
-
USAGE:
|
|
84
|
-
eventmodeler help <topic> Show a specific topic
|
|
85
|
-
eventmodeler help <search words> Search topics by keyword
|
|
86
|
-
|
|
87
|
-
TOPICS:
|
|
88
|
-
${TOPICS.map(t => ` ${t.meta.name.padEnd(24)} ${t.meta.description}`).join('\n')}
|
|
89
|
-
|
|
90
|
-
EXAMPLES:
|
|
91
|
-
eventmodeler help build-slice
|
|
92
|
-
eventmodeler help json
|
|
93
|
-
eventmodeler help place move canvas
|
|
94
|
-
eventmodeler help scenario given when then
|
|
95
|
-
`);
|
|
96
|
-
}
|