@formicoidea/labre-framework-ddd-event-storming 0.32.0 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +29 -0
- package/dist/actions.js +78 -0
- package/dist/background.d.ts +2 -0
- package/dist/background.js +138 -0
- package/dist/commands-manifest.d.ts +18 -0
- package/dist/commands-manifest.js +106 -0
- package/dist/commands.js +46 -5
- package/dist/descriptor.d.ts +7 -2
- package/dist/descriptor.js +5 -2
- package/dist/element-renderer.d.ts +17 -0
- package/dist/element-renderer.js +15 -0
- package/dist/element-view.d.ts +18 -0
- package/dist/element-view.js +19 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +7 -2
- package/dist/legend.d.ts +16 -0
- package/dist/legend.js +58 -0
- package/dist/nudges.d.ts +26 -0
- package/dist/nudges.js +73 -0
- package/dist/profiles.d.ts +2 -0
- package/dist/profiles.js +121 -0
- package/dist/roles.d.ts +62 -0
- package/dist/roles.js +84 -0
- package/dist/rules.d.ts +20 -0
- package/dist/rules.js +241 -0
- package/dist/toolbar/board-config.d.ts +29 -0
- package/dist/toolbar/board-config.js +86 -0
- package/dist/toolbar/event-storming-menu.d.ts +1 -1
- package/dist/toolbar/event-storming-menu.js +1 -1
- package/dist/toolbar/senior-button.d.ts +1 -0
- package/dist/toolbar/senior-button.js +1 -0
- package/dist/toolbar/senior-tool.js +1 -0
- package/dist/translations.d.ts +7 -5
- package/dist/translations.js +20 -7
- package/dist/view.d.ts +17 -1
- package/dist/view.js +61 -3
- package/package.json +7 -3
package/dist/legend.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ES_HOTSPOT, ES_STICKIES, LABEL_COLOR, roleLabel, } from '@formicoidea/labre-ddd-shared';
|
|
2
|
+
import { ES_ROLE, ES_STICKY_ROLE, EVENT_STORMING_ROLES } from './roles.js';
|
|
3
|
+
/**
|
|
4
|
+
* What the Event Storming board's automatic legend can say — a TABLE, and
|
|
5
|
+
* nothing else: the scan, the placement and the box are `createAutoLegend`'s
|
|
6
|
+
* job.
|
|
7
|
+
*
|
|
8
|
+
* Every row is DERIVED from the palette the stickies are drawn with
|
|
9
|
+
* ({@link ES_STICKIES}, {@link ES_HOTSPOT}) and from the role vocabulary's own
|
|
10
|
+
* labels, never restated — so the colour ladder the legend shows is by
|
|
11
|
+
* construction the colour ladder on the wall, and a tenth sticky kind added to
|
|
12
|
+
* `ES_STICKIES` gets its legend row with no edit.
|
|
13
|
+
*
|
|
14
|
+
* The hotspot is appended for the same reason `roles.ts` appends it: it lives in
|
|
15
|
+
* its own preset (a diamond, not a square) rather than in the table.
|
|
16
|
+
*/
|
|
17
|
+
export const EVENT_STORMING_AUTO_LEGEND = {
|
|
18
|
+
title: 'Legend',
|
|
19
|
+
roles: EVENT_STORMING_ROLES,
|
|
20
|
+
sections: [
|
|
21
|
+
{
|
|
22
|
+
title: 'Stickies',
|
|
23
|
+
entries: [
|
|
24
|
+
...ES_STICKIES.map(preset => ({
|
|
25
|
+
role: ES_STICKY_ROLE[preset.kind],
|
|
26
|
+
row: {
|
|
27
|
+
swatch: 'square',
|
|
28
|
+
color: preset.fill,
|
|
29
|
+
label: preset.label,
|
|
30
|
+
},
|
|
31
|
+
})),
|
|
32
|
+
{
|
|
33
|
+
role: ES_STICKY_ROLE.hotspot,
|
|
34
|
+
row: {
|
|
35
|
+
swatch: 'square',
|
|
36
|
+
color: ES_HOTSPOT.fill,
|
|
37
|
+
label: ES_HOTSPOT.label,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
title: 'Flow',
|
|
44
|
+
entries: [
|
|
45
|
+
{
|
|
46
|
+
role: ES_ROLE.flow,
|
|
47
|
+
row: {
|
|
48
|
+
swatch: 'line',
|
|
49
|
+
// The style `activateEventStormingFlow` arms the connector tool
|
|
50
|
+
// with: a solid line in the label colour.
|
|
51
|
+
color: LABEL_COLOR,
|
|
52
|
+
label: roleLabel(EVENT_STORMING_ROLES, ES_ROLE.flow),
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
package/dist/nudges.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { QualityNudge } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
/**
|
|
3
|
+
* Event Storming **board quality** — the checklist (WS5, level 2).
|
|
4
|
+
*
|
|
5
|
+
* `./rules.ts` is level 1: deterministic, decidable, real time. This file is
|
|
6
|
+
* everything a board needs that no algorithm can decide. The split is the
|
|
7
|
+
* taxonomy's whole point — ticking is assuming, never verifying.
|
|
8
|
+
*
|
|
9
|
+
* ## The past tense lives HERE, and that was an arbitration
|
|
10
|
+
*
|
|
11
|
+
* "Order placed" is a domain event; "Place order" is a command. It is the first
|
|
12
|
+
* thing a facilitator corrects and the most tempting rule in the whole plan —
|
|
13
|
+
* and it is a nudge, by PO arbitration of 26/08/2026, because deciding it means
|
|
14
|
+
* parsing a human sentence. In whatever language the room speaks. Written in
|
|
15
|
+
* marker pen. By somebody who abbreviates. A regular expression over `Y.Text`
|
|
16
|
+
* would be wrong every fifth sticky, and a validation platform gets exactly one
|
|
17
|
+
* chance to be wrong about something the user can see is right.
|
|
18
|
+
*
|
|
19
|
+
* So it is a tick, and the tick is honest: it says the room LOOKED, which is
|
|
20
|
+
* what the facilitator wanted anyway.
|
|
21
|
+
*
|
|
22
|
+
* Registered from the flag-gated `DddEventStormingViewExtension`: a checklist
|
|
23
|
+
* is tooling. Switching the flag off takes it away and leaves the ticks written
|
|
24
|
+
* on the board, unread, until it comes back.
|
|
25
|
+
*/
|
|
26
|
+
export declare const EVENT_STORMING_NUDGES: readonly QualityNudge[];
|
package/dist/nudges.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Storming **board quality** — the checklist (WS5, level 2).
|
|
3
|
+
*
|
|
4
|
+
* `./rules.ts` is level 1: deterministic, decidable, real time. This file is
|
|
5
|
+
* everything a board needs that no algorithm can decide. The split is the
|
|
6
|
+
* taxonomy's whole point — ticking is assuming, never verifying.
|
|
7
|
+
*
|
|
8
|
+
* ## The past tense lives HERE, and that was an arbitration
|
|
9
|
+
*
|
|
10
|
+
* "Order placed" is a domain event; "Place order" is a command. It is the first
|
|
11
|
+
* thing a facilitator corrects and the most tempting rule in the whole plan —
|
|
12
|
+
* and it is a nudge, by PO arbitration of 26/08/2026, because deciding it means
|
|
13
|
+
* parsing a human sentence. In whatever language the room speaks. Written in
|
|
14
|
+
* marker pen. By somebody who abbreviates. A regular expression over `Y.Text`
|
|
15
|
+
* would be wrong every fifth sticky, and a validation platform gets exactly one
|
|
16
|
+
* chance to be wrong about something the user can see is right.
|
|
17
|
+
*
|
|
18
|
+
* So it is a tick, and the tick is honest: it says the room LOOKED, which is
|
|
19
|
+
* what the facilitator wanted anyway.
|
|
20
|
+
*
|
|
21
|
+
* Registered from the flag-gated `DddEventStormingViewExtension`: a checklist
|
|
22
|
+
* is tooling. Switching the flag off takes it away and leaves the ticks written
|
|
23
|
+
* on the board, unread, until it comes back.
|
|
24
|
+
*/
|
|
25
|
+
export const EVENT_STORMING_NUDGES = [
|
|
26
|
+
{
|
|
27
|
+
/** See the header: the rule that is deliberately not a rule. */
|
|
28
|
+
id: 'es.q1-events-past-tense',
|
|
29
|
+
framework: 'ddd-event-storming',
|
|
30
|
+
labelKey: 'com.labre.event-storming.quality.events-past-tense',
|
|
31
|
+
fallback: 'Domain events are named in the past tense — something that happened, not something to do.',
|
|
32
|
+
order: 1,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
/**
|
|
36
|
+
* `es.against-timeline` judges each arc against the axis; nothing can judge
|
|
37
|
+
* whether the frieze as a whole tells the story the room means. Reading it
|
|
38
|
+
* out loud, left to right, is the move that finds the missing half of it.
|
|
39
|
+
*/
|
|
40
|
+
id: 'es.q2-timeline-read',
|
|
41
|
+
framework: 'ddd-event-storming',
|
|
42
|
+
labelKey: 'com.labre.event-storming.quality.timeline-read',
|
|
43
|
+
fallback: 'The timeline has been read out loud left to right, and reordered where it did not hold.',
|
|
44
|
+
order: 2,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'es.q3-hotspots-discussed',
|
|
48
|
+
framework: 'ddd-event-storming',
|
|
49
|
+
labelKey: 'com.labre.event-storming.quality.hotspots-discussed',
|
|
50
|
+
fallback: 'Every hotspot has been discussed — resolved, or assumed on purpose with a name against it.',
|
|
51
|
+
order: 3,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'es.q4-actors-and-systems',
|
|
55
|
+
framework: 'ddd-event-storming',
|
|
56
|
+
labelKey: 'com.labre.event-storming.quality.actors-and-systems',
|
|
57
|
+
fallback: 'The actors and the external systems are identified: every command has someone who issues it.',
|
|
58
|
+
order: 4,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
/**
|
|
62
|
+
* A pivotal event is a marking CONVENTION — a vertical bar, a bigger
|
|
63
|
+
* sticky, a line drawn on the paper — and the canvas cannot tell one from a
|
|
64
|
+
* sticky somebody happened to enlarge. What matters is that the room agreed
|
|
65
|
+
* which moments split the story, which is a conversation and not a shape.
|
|
66
|
+
*/
|
|
67
|
+
id: 'es.q5-pivotal-events',
|
|
68
|
+
framework: 'ddd-event-storming',
|
|
69
|
+
labelKey: 'com.labre.event-storming.quality.pivotal-events',
|
|
70
|
+
fallback: 'The pivotal events are marked: the board says where the story changes phase.',
|
|
71
|
+
order: 5,
|
|
72
|
+
},
|
|
73
|
+
];
|
package/dist/profiles.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Storming validation profiles (WS5).
|
|
3
|
+
*
|
|
4
|
+
* DATA owned by the framework, like its rules and its roles. A profile is
|
|
5
|
+
* chosen per BOARD, not per document, and the choice rides on the board element
|
|
6
|
+
* itself — a Big Picture and a Process-modelling board coexist on one canvas at
|
|
7
|
+
* two levels of requirement, which is precisely how the workshop goes.
|
|
8
|
+
*
|
|
9
|
+
* ## Three, and why three (PO arbitration, 26/08/2026)
|
|
10
|
+
*
|
|
11
|
+
* Every other framework in this library ships two. Event Storming gets three
|
|
12
|
+
* because it is not one activity, it is a SEQUENCE of them, and the same wall
|
|
13
|
+
* means different things at each stage:
|
|
14
|
+
*
|
|
15
|
+
* 1. **Big Picture (Sketch)** — everything on the wall at once, out of order,
|
|
16
|
+
* the grammar not yet decided. The linter must be silent;
|
|
17
|
+
* 2. **Process modelling** — the frieze gets read left to right and reordered.
|
|
18
|
+
* Time starts to mean something; the grammar still does not;
|
|
19
|
+
* 3. **Software design** — the sentences are supposed to be sentences.
|
|
20
|
+
*
|
|
21
|
+
* Two profiles would have forced stage 2 to pick a side: either the timeline
|
|
22
|
+
* goes unchecked through the very session that is about ordering it, or the
|
|
23
|
+
* grammar starts firing at a wall that has not been cleaned up yet. The middle
|
|
24
|
+
* profile exists because the middle stage exists.
|
|
25
|
+
*
|
|
26
|
+
* Registered from the flag-gated `DddEventStormingViewExtension`, beside the
|
|
27
|
+
* rules: switching the flag off takes the choice away with the rest of the
|
|
28
|
+
* tooling, and a board already set to `design` simply stops being checked until
|
|
29
|
+
* it comes back — the id stays written, untouched.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Sketch — the Big Picture. Every rule demoted to `audit`: findings still reach
|
|
33
|
+
* `violations$` for a host panel and a conformance report, and the canvas says
|
|
34
|
+
* nothing.
|
|
35
|
+
*
|
|
36
|
+
* Named **"Big Picture (Sketch)"** in the dropdown, both words on purpose (PO
|
|
37
|
+
* recette, 26/08/2026). "Sketch" is the vocabulary every framework in this
|
|
38
|
+
* library shares for its quietest level, and it is the word the recette script
|
|
39
|
+
* uses; "Big Picture" is the word the workshop uses for this stage and the only
|
|
40
|
+
* one a facilitator would look for. Carrying both is what lets the two readings
|
|
41
|
+
* meet on one entry instead of the user having to learn which name this
|
|
42
|
+
* framework chose.
|
|
43
|
+
*
|
|
44
|
+
* The DEFAULT, and more deliberately here than anywhere else in the library. A
|
|
45
|
+
* Big Picture is **supposed to be chaotic**: that is the method, not a failure
|
|
46
|
+
* of it. Events go up out of order because remembering is not sorting, arcs get
|
|
47
|
+
* drawn between things nobody has typed yet, and stickies get stacked three
|
|
48
|
+
* deep while a stack is what somebody means. A tool arguing with that hand is
|
|
49
|
+
* not strict, it is wrong — it is judging a stage of the workshop by the
|
|
50
|
+
* criteria of a later one.
|
|
51
|
+
*
|
|
52
|
+
* Being the default also means it WRITES NOTHING: a board on `sketch` carries no
|
|
53
|
+
* profile key, so every board ever stormed is on it, with no migration and no
|
|
54
|
+
* backfill.
|
|
55
|
+
*/
|
|
56
|
+
const sketch = {
|
|
57
|
+
id: 'es.sketch',
|
|
58
|
+
framework: 'ddd-event-storming',
|
|
59
|
+
labelKey: 'com.labre.event-storming.profile.sketch',
|
|
60
|
+
fallback: 'Big Picture (Sketch)',
|
|
61
|
+
isDefault: true,
|
|
62
|
+
rules: {
|
|
63
|
+
'es.against-timeline': 'audit',
|
|
64
|
+
'es.forbidden-arc': 'audit',
|
|
65
|
+
'es.overlapping-stickies': 'audit',
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Process — the frieze is being read left to right and reordered.
|
|
70
|
+
*
|
|
71
|
+
* The timeline is the ONLY thing promoted, because ordering the frieze is
|
|
72
|
+
* exactly what this stage is for: an arc running backwards is now the finding
|
|
73
|
+
* the session is looking for, and the tool pointing at it is the tool helping.
|
|
74
|
+
*
|
|
75
|
+
* The grammar stays at `audit`, on purpose. The wall is still full of stickies
|
|
76
|
+
* whose kind nobody has settled — an event that is going to turn out to be a
|
|
77
|
+
* command, a read model somebody drew as an aggregate — and firing
|
|
78
|
+
* `forbidden-arc` at that is arguing about a sentence whose words are still
|
|
79
|
+
* being chosen. Overlaps stay quiet for the same reason: things are being
|
|
80
|
+
* MOVED, and everything overlaps while it is being moved.
|
|
81
|
+
*/
|
|
82
|
+
const process = {
|
|
83
|
+
id: 'es.process',
|
|
84
|
+
framework: 'ddd-event-storming',
|
|
85
|
+
labelKey: 'com.labre.event-storming.profile.process',
|
|
86
|
+
fallback: 'Process modelling',
|
|
87
|
+
rules: {
|
|
88
|
+
'es.against-timeline': 'warning',
|
|
89
|
+
'es.forbidden-arc': 'audit',
|
|
90
|
+
'es.overlapping-stickies': 'audit',
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Design — the deliverable level: a board that leaves the room and is read by
|
|
95
|
+
* somebody who was not in it.
|
|
96
|
+
*
|
|
97
|
+
* All three at `warning`. By this stage every sticky has a kind, every arc is a
|
|
98
|
+
* claim about the model, and a frieze nobody can read is a frieze that will be
|
|
99
|
+
* misread. Every severity is spelled out rather than left absent, so the answer
|
|
100
|
+
* is readable in one place.
|
|
101
|
+
*
|
|
102
|
+
* Nothing here is `blocking-overridable`: nothing in this library implements
|
|
103
|
+
* refusal, and declaring it would be data claiming an effect that does not
|
|
104
|
+
* exist.
|
|
105
|
+
*/
|
|
106
|
+
const design = {
|
|
107
|
+
id: 'es.design',
|
|
108
|
+
framework: 'ddd-event-storming',
|
|
109
|
+
labelKey: 'com.labre.event-storming.profile.design',
|
|
110
|
+
fallback: 'Software design',
|
|
111
|
+
rules: {
|
|
112
|
+
'es.against-timeline': 'warning',
|
|
113
|
+
'es.forbidden-arc': 'warning',
|
|
114
|
+
'es.overlapping-stickies': 'warning',
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
export const EVENT_STORMING_PROFILES = [
|
|
118
|
+
sketch,
|
|
119
|
+
process,
|
|
120
|
+
design,
|
|
121
|
+
];
|
package/dist/roles.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ES_STICKIES } from '@formicoidea/labre-ddd-shared';
|
|
2
|
+
import type { RoleDefs } from '@formicoidea/labre-core/std/gfx';
|
|
3
|
+
/**
|
|
4
|
+
* Event Storming role vocabulary (WS5).
|
|
5
|
+
*
|
|
6
|
+
* A role is the semantic identity of an artefact — no rule will ever look at a
|
|
7
|
+
* shape type or at a fill colour. Three families:
|
|
8
|
+
*
|
|
9
|
+
* - the **board** (`es:board`), the paper roll the storm is run on;
|
|
10
|
+
* - the **sticky** (`es:sticky`), parent of the nine kinds the notation has;
|
|
11
|
+
* - the **flow** (`es:flow`), the edge that says one thing leads to another.
|
|
12
|
+
*
|
|
13
|
+
* ## Why a sticky PARENT
|
|
14
|
+
*
|
|
15
|
+
* Because two of the three rules are written on it and neither wants to know
|
|
16
|
+
* about the nine. `es.overlapping-stickies` is a readability rule — any two
|
|
17
|
+
* stickies on top of each other are unreadable, whichever kinds they are — and
|
|
18
|
+
* the day a tenth kind lands it is covered without a line changing.
|
|
19
|
+
* Specialisation is DATA (`parent`), read by `roleIsA`.
|
|
20
|
+
*
|
|
21
|
+
* ## The alphabet is smaller than the vocabulary
|
|
22
|
+
*
|
|
23
|
+
* All nine kinds get a role; only seven of them appear in the grammar
|
|
24
|
+
* (`es.forbidden-arc`). The **hotspot** and the **constraint** are declared here
|
|
25
|
+
* and cited by no triplet, deliberately: a hotspot is the workshop saying "we do
|
|
26
|
+
* not know", and a flow drawn onto one is somebody parking a question, not
|
|
27
|
+
* making a claim. Being outside the alphabet takes the whole edge out of the
|
|
28
|
+
* conversation (see `RelationEndpointsDef.allowed`), which is exactly the
|
|
29
|
+
* proportionality the family was built for.
|
|
30
|
+
*
|
|
31
|
+
* ## Compat
|
|
32
|
+
*
|
|
33
|
+
* Nothing is backfilled. Stickies placed before these roles existed carry none
|
|
34
|
+
* and are never evaluated (promesse #71): they stay drawings, in the documents
|
|
35
|
+
* they are in.
|
|
36
|
+
*/
|
|
37
|
+
/** The sticky kinds as used at the creation sites, hotspot included. */
|
|
38
|
+
export type EventStormingStickyKind = (typeof ES_STICKIES)[number]['kind'] | 'hotspot';
|
|
39
|
+
export type EventStormingRoleId = `es:${string}`;
|
|
40
|
+
/**
|
|
41
|
+
* Role id per sticky kind, DERIVED from the shared preset table rather than
|
|
42
|
+
* restated — so the day a tenth sticky lands in `ES_STICKIES` it gets its role
|
|
43
|
+
* here with no edit. The hotspot is appended because it lives in its own preset
|
|
44
|
+
* (`ES_HOTSPOT`, a diamond rather than a square) and not in the table.
|
|
45
|
+
*/
|
|
46
|
+
export declare const ES_STICKY_ROLE: Record<EventStormingStickyKind, EventStormingRoleId>;
|
|
47
|
+
/** The three roles that are not a sticky kind, plus the nine that are. */
|
|
48
|
+
export declare const ES_ROLE: {
|
|
49
|
+
readonly domainEvent: `es:${string}`;
|
|
50
|
+
readonly command: `es:${string}`;
|
|
51
|
+
readonly aggregate: `es:${string}`;
|
|
52
|
+
readonly actor: `es:${string}`;
|
|
53
|
+
readonly constraint: `es:${string}`;
|
|
54
|
+
readonly policy: `es:${string}`;
|
|
55
|
+
readonly readModel: `es:${string}`;
|
|
56
|
+
readonly system: `es:${string}`;
|
|
57
|
+
readonly hotspot: `es:${string}`;
|
|
58
|
+
readonly board: "es:board";
|
|
59
|
+
readonly sticky: "es:sticky";
|
|
60
|
+
readonly flow: "es:flow";
|
|
61
|
+
};
|
|
62
|
+
export declare const EVENT_STORMING_ROLES: RoleDefs;
|
package/dist/roles.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ES_HOTSPOT, ES_STICKIES } from '@formicoidea/labre-ddd-shared';
|
|
2
|
+
/** camelCase → kebab-case, the WS2 derivation. */
|
|
3
|
+
const kebab = (kind) => kind.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
|
|
4
|
+
/**
|
|
5
|
+
* The one kind whose role id is not its kind kebab-cased.
|
|
6
|
+
*
|
|
7
|
+
* `system` is what the palette has called the sticky since the tool shipped and
|
|
8
|
+
* what its telemetry emits; `external-system` is what Event Storming calls the
|
|
9
|
+
* thing, and it is the word a rule and a hover reveal have to say. Renaming the
|
|
10
|
+
* kind would move a telemetry value (ADR 0008 forbids it) and renaming the role
|
|
11
|
+
* would make the grammar read `command → system`, which is not a sentence
|
|
12
|
+
* anybody says. One entry in a table is the cost of keeping both right.
|
|
13
|
+
*/
|
|
14
|
+
const ROLE_SLUG = {
|
|
15
|
+
system: 'external-system',
|
|
16
|
+
};
|
|
17
|
+
const slug = (kind) => ROLE_SLUG[kind] ?? kebab(kind);
|
|
18
|
+
/**
|
|
19
|
+
* Role id per sticky kind, DERIVED from the shared preset table rather than
|
|
20
|
+
* restated — so the day a tenth sticky lands in `ES_STICKIES` it gets its role
|
|
21
|
+
* here with no edit. The hotspot is appended because it lives in its own preset
|
|
22
|
+
* (`ES_HOTSPOT`, a diamond rather than a square) and not in the table.
|
|
23
|
+
*/
|
|
24
|
+
export const ES_STICKY_ROLE = Object.fromEntries([
|
|
25
|
+
...ES_STICKIES.map(preset => [preset.kind, `es:${slug(preset.kind)}`]),
|
|
26
|
+
['hotspot', 'es:hotspot'],
|
|
27
|
+
]);
|
|
28
|
+
/** The three roles that are not a sticky kind, plus the nine that are. */
|
|
29
|
+
export const ES_ROLE = {
|
|
30
|
+
board: 'es:board',
|
|
31
|
+
sticky: 'es:sticky',
|
|
32
|
+
flow: 'es:flow',
|
|
33
|
+
...ES_STICKY_ROLE,
|
|
34
|
+
};
|
|
35
|
+
/** Human label per kind, from the same presets the palette renders. */
|
|
36
|
+
const STICKY_LABEL = {
|
|
37
|
+
...Object.fromEntries(ES_STICKIES.map(preset => [preset.kind, preset.label])),
|
|
38
|
+
hotspot: ES_HOTSPOT.label,
|
|
39
|
+
};
|
|
40
|
+
const DEFS = [
|
|
41
|
+
// The board: a frame, not a sticky. It specialises nothing, so a rule written
|
|
42
|
+
// on `es:sticky` can never fall on the roll its subjects are stuck to.
|
|
43
|
+
{
|
|
44
|
+
id: ES_ROLE.board,
|
|
45
|
+
kind: 'node',
|
|
46
|
+
labelKey: 'com.labre.event-storming.role.board',
|
|
47
|
+
labelFallback: 'Event Storming board',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: ES_ROLE.sticky,
|
|
51
|
+
kind: 'node',
|
|
52
|
+
labelKey: 'com.labre.event-storming.role.sticky',
|
|
53
|
+
labelFallback: 'Sticky',
|
|
54
|
+
},
|
|
55
|
+
...Object.keys(STICKY_LABEL).map((kind) => ({
|
|
56
|
+
id: ES_STICKY_ROLE[kind],
|
|
57
|
+
parent: ES_ROLE.sticky,
|
|
58
|
+
kind: 'node',
|
|
59
|
+
labelKey: `com.labre.event-storming.role.${slug(kind)}`,
|
|
60
|
+
labelFallback: STICKY_LABEL[kind],
|
|
61
|
+
})),
|
|
62
|
+
/**
|
|
63
|
+
* The flow. Tier 1 of `docs/adr/0010`: the source is the subject of the verb,
|
|
64
|
+
* so the source is what happens FIRST. Everything downstream of that sentence
|
|
65
|
+
* — the tool hint, the hover reveal, the inversion command and
|
|
66
|
+
* `es.against-timeline` — reads this one declaration and never a framework
|
|
67
|
+
* name.
|
|
68
|
+
*/
|
|
69
|
+
{
|
|
70
|
+
id: ES_ROLE.flow,
|
|
71
|
+
kind: 'edge',
|
|
72
|
+
labelKey: 'com.labre.event-storming.role.flow',
|
|
73
|
+
labelFallback: 'Flow',
|
|
74
|
+
direction: {
|
|
75
|
+
verbKey: 'com.labre.event-storming.role.flow.verb',
|
|
76
|
+
verbFallback: 'leads to',
|
|
77
|
+
gestureHintKey: 'com.labre.event-storming.role.flow.gesture',
|
|
78
|
+
gestureHintFallback: 'Drag from what happens first to what follows.',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
// Null prototype: this is a lookup table keyed by ids that may one day come
|
|
83
|
+
// from host-supplied packs, so `defs['toString']` must not resolve.
|
|
84
|
+
export const EVENT_STORMING_ROLES = Object.assign(Object.create(null), Object.fromEntries(DEFS.map(def => [def.id, def])));
|
package/dist/rules.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { EndpointTriplet, ValidationRule } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
/**
|
|
3
|
+
* The canonical Event Storming sentence, as a matrix.
|
|
4
|
+
*
|
|
5
|
+
* Read aloud, which is how it was checked: an actor issues a command; a command
|
|
6
|
+
* lands on an aggregate, or on an external system; an aggregate raises a domain
|
|
7
|
+
* event, and so does an external system; a domain event triggers a policy, and
|
|
8
|
+
* a domain event feeds a read model; a policy issues a command; a read model
|
|
9
|
+
* informs an actor.
|
|
10
|
+
*
|
|
11
|
+
* Nine sentences and no more. Every one of them is in Brandolini's own picture
|
|
12
|
+
* of the "Big Picture → Process modelling" grammar, and nothing has been added
|
|
13
|
+
* because it seemed plausible: a matrix that sanctions a sentence the notation
|
|
14
|
+
* does not say is worse than no matrix, because the sentence it fails to catch
|
|
15
|
+
* is the one somebody will ship.
|
|
16
|
+
*
|
|
17
|
+
* Exported so a test asserts THIS table rather than a copy of it.
|
|
18
|
+
*/
|
|
19
|
+
export declare const ES_FLOW_MATRIX: readonly EndpointTriplet[];
|
|
20
|
+
export declare const EVENT_STORMING_RULES: readonly ValidationRule[];
|