eva4j 1.0.16 → 1.0.18
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/AGENTS.md +220 -5
- package/DOMAIN_YAML_GUIDE.md +188 -3
- package/FUTURE_FEATURES.md +33 -52
- package/QUICK_REFERENCE.md +8 -4
- package/bin/eva4j.js +70 -2
- package/config/defaults.json +1 -0
- package/docs/CAMUNDA_DMN_GUIDE.md +1380 -0
- package/docs/KAFKA_PRODUCTION_CONFIG.md +441 -0
- package/docs/RABBITMQ_PRODUCTION_CONFIG.md +227 -0
- package/docs/commands/ADD_RABBITMQ_CLIENT.md +192 -0
- package/docs/commands/EVALUATE_SYSTEM.md +290 -10
- package/docs/commands/GENERATE_RABBITMQ_EVENT.md +341 -0
- package/docs/commands/GENERATE_RABBITMQ_LISTENER.md +595 -0
- package/docs/commands/GENERATE_TEMPORAL_FLOW.md +52 -12
- package/docs/commands/INDEX.md +27 -3
- package/docs/prototype/TEMPORAL_COMMUNICATION_PATTERNS.md +731 -0
- package/docs/prototype/TEMPORAL_DESIGN_METHODOLOGY.md +740 -0
- package/docs/prototype/system/RISKS.md +277 -0
- package/docs/prototype/system/customers.yaml +133 -0
- package/docs/prototype/system/inventory.yaml +109 -0
- package/docs/prototype/system/notifications.yaml +131 -0
- package/docs/prototype/system/orders.yaml +241 -0
- package/docs/prototype/system/payments.yaml +256 -0
- package/docs/prototype/system/products.yaml +168 -0
- package/docs/prototype/system/system.yaml +269 -0
- package/examples/domain-endpoints-multi-aggregate.yaml +140 -0
- package/examples/domain-events.yaml +26 -0
- package/examples/domain-read-models.yaml +113 -0
- package/examples/system/customer.yaml +89 -0
- package/examples/system/orders.yaml +119 -0
- package/examples/system/product.yaml +27 -0
- package/examples/system/system.yaml +80 -0
- package/package.json +1 -1
- package/read-model-spec.md +664 -0
- package/src/agents/design-gap-analyst-temporal.agent.md +452 -0
- package/src/agents/design-gap-analyst.agent.md +383 -0
- package/src/agents/design-reviewer-temporal.agent.md +412 -0
- package/src/agents/design-reviewer.agent.md +34 -5
- package/src/agents/implement-use-cases.prompt.md +179 -0
- package/src/agents/ux-gap-analyst.agent.md +412 -0
- package/src/commands/add-rabbitmq-client.js +261 -0
- package/src/commands/add-temporal-client.js +22 -2
- package/src/commands/build.js +267 -11
- package/src/commands/evaluate-system.js +700 -13
- package/src/commands/generate-entities.js +560 -24
- package/src/commands/generate-http-exchange.js +3 -0
- package/src/commands/generate-kafka-event.js +3 -0
- package/src/commands/generate-kafka-listener.js +3 -0
- package/src/commands/generate-rabbitmq-event.js +665 -0
- package/src/commands/generate-rabbitmq-listener.js +205 -0
- package/src/commands/generate-record.js +2 -2
- package/src/commands/generate-resource.js +4 -1
- package/src/commands/generate-temporal-activity.js +970 -33
- package/src/commands/generate-temporal-flow.js +98 -38
- package/src/commands/generate-temporal-system.js +708 -0
- package/src/commands/generate-usecase.js +4 -1
- package/src/skills/build-system-yaml/SKILL.md +343 -2
- package/src/skills/build-system-yaml/references/domain-yaml-spec.md +253 -26
- package/src/skills/build-system-yaml/references/module-spec.md +90 -9
- package/src/skills/build-system-yaml/references/system-yaml-spec.md +36 -0
- package/src/skills/build-temporal-system/SKILL.md +752 -0
- package/src/skills/build-temporal-system/references/temporal-communication-patterns.md +167 -0
- package/src/skills/build-temporal-system/references/temporal-domain-yaml-spec.md +449 -0
- package/src/skills/build-temporal-system/references/temporal-module-spec.md +353 -0
- package/src/skills/build-temporal-system/references/temporal-system-yaml-spec.md +326 -0
- package/src/skills/implement-use-case/SKILL.md +350 -0
- package/src/skills/implement-use-case/references/use-case-patterns.md +980 -0
- package/src/skills/requirements-elicitation/SKILL.md +228 -0
- package/src/skills/requirements-elicitation/references/interview-framework.md +260 -0
- package/src/skills/requirements-elicitation/references/output-templates.md +368 -0
- package/src/utils/bounded-context-diagram.js +844 -0
- package/src/utils/config-manager.js +4 -2
- package/src/utils/domain-validator.js +495 -17
- package/src/utils/naming.js +20 -0
- package/src/utils/system-validator.js +169 -11
- package/src/utils/system-yaml-parser.js +318 -0
- package/src/utils/temporal-validator.js +497 -0
- package/src/utils/validator.js +3 -1
- package/src/utils/yaml-to-entity.js +281 -9
- package/templates/aggregate/AggregateRepository.java.ejs +4 -0
- package/templates/aggregate/AggregateRepositoryImpl.java.ejs +8 -0
- package/templates/aggregate/AggregateRoot.java.ejs +38 -4
- package/templates/aggregate/DomainEventHandler.java.ejs +116 -22
- package/templates/aggregate/JpaAggregateRoot.java.ejs +4 -4
- package/templates/aggregate/JpaEntity.java.ejs +2 -2
- package/templates/base/docker/rabbitmq-services.yaml.ejs +12 -0
- package/templates/base/resources/parameters/develop/kafka.yaml.ejs +5 -0
- package/templates/base/resources/parameters/develop/rabbitmq.yaml.ejs +15 -0
- package/templates/base/resources/parameters/develop/temporal.yaml.ejs +0 -3
- package/templates/base/resources/parameters/local/kafka.yaml.ejs +5 -0
- package/templates/base/resources/parameters/local/rabbitmq.yaml.ejs +15 -0
- package/templates/base/resources/parameters/local/temporal.yaml.ejs +0 -3
- package/templates/base/resources/parameters/production/kafka.yaml.ejs +39 -8
- package/templates/base/resources/parameters/production/rabbitmq.yaml.ejs +32 -0
- package/templates/base/resources/parameters/production/temporal.yaml.ejs +0 -3
- package/templates/base/resources/parameters/test/kafka.yaml.ejs +12 -6
- package/templates/base/resources/parameters/test/rabbitmq.yaml.ejs +15 -0
- package/templates/base/resources/parameters/test/temporal.yaml.ejs +0 -3
- package/templates/base/root/AGENTS.md.ejs +1 -1
- package/templates/crud/DeleteCommandHandler.java.ejs +19 -1
- package/templates/crud/EndpointsController.java.ejs +1 -1
- package/templates/crud/ScaffoldCommand.java.ejs +5 -2
- package/templates/crud/ScaffoldCommandHandler.java.ejs +3 -1
- package/templates/crud/ScaffoldQuery.java.ejs +5 -2
- package/templates/crud/ScaffoldQueryHandler.java.ejs +3 -1
- package/templates/crud/SubEntityRemoveCommand.java.ejs +1 -1
- package/templates/crud/UpdateCommandHandler.java.ejs +53 -2
- package/templates/evaluate/report.html.ejs +1447 -90
- package/templates/kafka-event/KafkaConfigBean.java.ejs +1 -1
- package/templates/kafka-event/KafkaMessageBroker.java.ejs +3 -3
- package/templates/ports/PortAclMapper.java.ejs +35 -0
- package/templates/ports/PortFeignAdapter.java.ejs +7 -22
- package/templates/ports/PortFeignClient.java.ejs +4 -0
- package/templates/ports/PortResponseDto.java.ejs +1 -1
- package/templates/rabbitmq-event/RabbitConfigBean.java.ejs +33 -0
- package/templates/rabbitmq-event/RabbitConfigExchange.java.ejs +12 -0
- package/templates/rabbitmq-event/RabbitMessageBroker.java.ejs +35 -0
- package/templates/rabbitmq-event/RabbitMessageBrokerMethod.java.ejs +9 -0
- package/templates/rabbitmq-listener/RabbitConfigConsumerBean.java.ejs +33 -0
- package/templates/rabbitmq-listener/RabbitConfigConsumerExchange.java.ejs +12 -0
- package/templates/rabbitmq-listener/RabbitListenerClass.java.ejs +82 -0
- package/templates/rabbitmq-listener/RabbitListenerSimple.java.ejs +56 -0
- package/templates/read-model/ReadModelDomain.java.ejs +46 -0
- package/templates/read-model/ReadModelJpa.java.ejs +58 -0
- package/templates/read-model/ReadModelJpaRepository.java.ejs +13 -0
- package/templates/read-model/ReadModelKafkaListener.java.ejs +64 -0
- package/templates/read-model/ReadModelRabbitListener.java.ejs +71 -0
- package/templates/read-model/ReadModelRepository.java.ejs +42 -0
- package/templates/read-model/ReadModelRepositoryImpl.java.ejs +85 -0
- package/templates/read-model/ReadModelSyncHandler.java.ejs +54 -0
- package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +18 -4
- package/templates/shared/configurations/rabbitmqConfig/RabbitMQConfig.java.ejs +100 -0
- package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +2 -64
- package/templates/shared/configurations/temporalConfig/TemporalWorkerFactoryLifecycle.java.ejs +41 -0
- package/templates/temporal-activity/ActivityImpl.java.ejs +68 -2
- package/templates/temporal-activity/ActivityInput.java.ejs +14 -0
- package/templates/temporal-activity/ActivityInterface.java.ejs +7 -1
- package/templates/temporal-activity/ActivityOutput.java.ejs +14 -0
- package/templates/temporal-activity/NestedType.java.ejs +12 -0
- package/templates/temporal-activity/SharedActivityInput.java.ejs +14 -0
- package/templates/temporal-activity/SharedActivityInterface.java.ejs +15 -0
- package/templates/temporal-activity/SharedActivityOutput.java.ejs +14 -0
- package/templates/temporal-activity/SharedNestedType.java.ejs +12 -0
- package/templates/temporal-flow/ModuleHeavyActivity.java.ejs +6 -0
- package/templates/temporal-flow/ModuleLightActivity.java.ejs +6 -0
- package/templates/temporal-flow/ModuleTemporalWorkerConfig.java.ejs +58 -0
- package/templates/temporal-flow/WorkFlowImpl.java.ejs +172 -12
- package/templates/temporal-flow/WorkFlowInput.java.ejs +11 -0
- package/templates/temporal-flow/WorkFlowInterface.java.ejs +5 -4
- package/templates/temporal-flow/WorkFlowService.java.ejs +42 -12
- package/COMMAND_EVALUATION.md +0 -911
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# Temporal Module Specification — Documentation Templates
|
|
2
|
+
|
|
3
|
+
Reference for generating `system/system.md` and `system/{module}.md` in a Temporal-based system.
|
|
4
|
+
|
|
5
|
+
> **PATH RULE:** All documentation files MUST be saved inside the `system/` directory (e.g., `system/system.md`, `system/orders.md`). NEVER save at the project root.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Part 1 — system.md (global narrative specification)
|
|
10
|
+
|
|
11
|
+
### Location: `system/system.md`
|
|
12
|
+
|
|
13
|
+
Narrative technical specification of the complete system. One `##` section per module with detailed subsections.
|
|
14
|
+
|
|
15
|
+
### Mandatory structure
|
|
16
|
+
|
|
17
|
+
```markdown
|
|
18
|
+
# system.md — Technical Specification
|
|
19
|
+
|
|
20
|
+
## {module-name}
|
|
21
|
+
|
|
22
|
+
### Module Role
|
|
23
|
+
[3–5 detailed paragraphs]
|
|
24
|
+
- What business problem it solves and what entities/concepts it manages
|
|
25
|
+
- Its exclusive responsibilities (bounded context boundaries)
|
|
26
|
+
- What is NOT this module's responsibility
|
|
27
|
+
- How it collaborates with other modules via Temporal activities
|
|
28
|
+
- Business invariants it protects
|
|
29
|
+
|
|
30
|
+
### Use Cases
|
|
31
|
+
[One `####` section per useCase in exposes:]
|
|
32
|
+
|
|
33
|
+
#### {UseCaseName}
|
|
34
|
+
**What it does:** [detailed business logic description]
|
|
35
|
+
**Preconditions:** [required state, entities that must exist]
|
|
36
|
+
**Postconditions:** [system state after successful execution]
|
|
37
|
+
**Validations and errors:** [exception conditions and error types]
|
|
38
|
+
**Events emitted:** [DomainEvent name and what workflow it triggers, or "none"]
|
|
39
|
+
**Operations:**
|
|
40
|
+
1. [Load entity / validate precondition — throw NotFoundException or 400 if invalid]
|
|
41
|
+
2. [External port call: {PortName}.{method}() — if applicable (**ONLY for external services** like payment gateways, NOT for other modules**)]
|
|
42
|
+
3. [Invoke domain method: entity.{method}()]
|
|
43
|
+
4. [Persist via repository]
|
|
44
|
+
5. [Emit event → triggers {WorkflowName} — if applicable]
|
|
45
|
+
|
|
46
|
+
> **⚠️ Important:** Handlers do NOT invoke activities from other modules (or their own). Cross-module data is obtained via activities orchestrated by workflows. If this use case needs external data, it should emit a Domain Event with `notifies:` that triggers a workflow containing the required read activities.
|
|
47
|
+
|
|
48
|
+
### Exposed Endpoints
|
|
49
|
+
[One `####` per endpoint in exposes:]
|
|
50
|
+
|
|
51
|
+
#### {METHOD} {/path}
|
|
52
|
+
**Purpose:** [endpoint description and usage context]
|
|
53
|
+
**Path params:** [param — Type — Description; omit if none]
|
|
54
|
+
**Query params:** [GET/DELETE only — param: Type, required/optional, default, description; omit if none]
|
|
55
|
+
**Request body:** [POST/PUT/PATCH only — field: Type — constraint; omit for GET/DELETE]
|
|
56
|
+
**Response:** [GET only — field: Type — business meaning; list endpoints include {content:[...], totalElements, page, size}]
|
|
57
|
+
**Errors:** [HTTP status — condition]
|
|
58
|
+
|
|
59
|
+
### Activities Exposed
|
|
60
|
+
[One `####` per activity declared in module's domain.yaml]
|
|
61
|
+
|
|
62
|
+
#### {ActivityName}
|
|
63
|
+
**Type:** `light` | `heavy`
|
|
64
|
+
**Task Queue:** `{MODULE}_LIGHT_TASK_QUEUE` | `{MODULE}_HEAVY_TASK_QUEUE`
|
|
65
|
+
**Invoked by:** [workflow name(s) from system.yaml or domain.yaml]
|
|
66
|
+
**Input:** [field names and types]
|
|
67
|
+
**Output:** [field names and types, or "void"]
|
|
68
|
+
**Compensation:** [{compensation activity name} or "none"]
|
|
69
|
+
**What it does:** [detailed description of the business operation]
|
|
70
|
+
**Data access:** [what this activity reads/writes in its own database]
|
|
71
|
+
|
|
72
|
+
### Workflows Triggered
|
|
73
|
+
[Only if module has events with notifies:]
|
|
74
|
+
|
|
75
|
+
#### {EventName} → {WorkflowName}
|
|
76
|
+
**When:** [exact business condition that fires the event]
|
|
77
|
+
**Workflow:** [{WorkflowName} — defined in system.yaml]
|
|
78
|
+
**Steps summary:** [brief description of what the workflow does]
|
|
79
|
+
|
|
80
|
+
### Single-Module Workflows
|
|
81
|
+
[Only if module has workflows: in its domain.yaml]
|
|
82
|
+
|
|
83
|
+
#### {WorkflowName}
|
|
84
|
+
**Trigger:** [what internal event or condition starts it]
|
|
85
|
+
**Task Queue:** [{QUEUE_NAME}]
|
|
86
|
+
**Steps:** [ordered list of activities with descriptions]
|
|
87
|
+
**Purpose:** [why this internal workflow exists]
|
|
88
|
+
|
|
89
|
+
### Ports (external services)
|
|
90
|
+
[Only if module has ports: in its domain.yaml]
|
|
91
|
+
|
|
92
|
+
#### {PortName} → {target}
|
|
93
|
+
**When called:** [in which activity or use case]
|
|
94
|
+
**Endpoints used:** [METHOD /path list]
|
|
95
|
+
**Data obtained and how it's used:** [detailed description]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Rules for system.md
|
|
99
|
+
|
|
100
|
+
- **Be EXTREMELY specific**: entity states, concrete validations, relevant fields
|
|
101
|
+
- **End-to-end flows**: if `ConfirmOrder` is invoked as an activity by `PlaceOrderWorkflow`, explain it in both sections
|
|
102
|
+
- **Activities are the collaboration mechanism**: explain which workflows invoke each activity
|
|
103
|
+
- **Reference modules by name**
|
|
104
|
+
- **State machines** when there are lifecycles
|
|
105
|
+
- **Detailed endpoints**: separate path params from query params. For GETs provide the key response fields with types. For POST/PUT/PATCH list body fields with types and constraints. Never use "see request" as a response description.
|
|
106
|
+
- **Operations in use cases**: the `**Operations:**` field is mandatory in each `#### {UseCaseName}`. Each item describes a concrete handler step referencing real repository, port, domain method, and event names. No generic items like "execute business logic".
|
|
107
|
+
- Omit non-applicable sections
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Part 2 — {module}.md (technical specification per module)
|
|
112
|
+
|
|
113
|
+
### Location: `system/{module-name}.md`
|
|
114
|
+
|
|
115
|
+
Complete, standalone specification for each module. A developer can read it without knowing the full system.
|
|
116
|
+
|
|
117
|
+
### Mandatory structure
|
|
118
|
+
|
|
119
|
+
```markdown
|
|
120
|
+
# {module-name} — Technical Specification
|
|
121
|
+
|
|
122
|
+
## Module Role
|
|
123
|
+
[3–5 VERY detailed paragraphs]
|
|
124
|
+
- What business problem it solves and what entities/concepts it manages
|
|
125
|
+
- Exclusive responsibilities (bounded context boundaries)
|
|
126
|
+
- What is NOT this module's responsibility
|
|
127
|
+
- How it collaborates with other modules via Temporal activities
|
|
128
|
+
- How it is different from a broker-based architecture
|
|
129
|
+
|
|
130
|
+
## Invariants
|
|
131
|
+
|
|
132
|
+
> Invariants are conditions that must be **always true** within this bounded context.
|
|
133
|
+
> Violating an invariant is a **critical business error** that must throw an exception.
|
|
134
|
+
|
|
135
|
+
| ID | Invariant | Violation consequence |
|
|
136
|
+
|----|-----------|----------------------|
|
|
137
|
+
| INV-01 | [condition] | [exception / what it prevents] |
|
|
138
|
+
|
|
139
|
+
> Each use case and activity must verify relevant invariants **before** persisting.
|
|
140
|
+
|
|
141
|
+
## State Machine
|
|
142
|
+
[ONLY if the module manages entity lifecycle — omit otherwise]
|
|
143
|
+
|
|
144
|
+
```mermaid
|
|
145
|
+
stateDiagram-v2
|
|
146
|
+
[*] --> INITIAL_STATE
|
|
147
|
+
INITIAL_STATE --> NEXT_STATE : event / action
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Interaction Diagram
|
|
151
|
+
|
|
152
|
+
> Shows: REST endpoints → use cases, activities invoked by workflows, events triggered.
|
|
153
|
+
> Uses Temporal-specific iconography.
|
|
154
|
+
|
|
155
|
+
```mermaid
|
|
156
|
+
flowchart TD
|
|
157
|
+
subgraph HTTP["REST Endpoints"]
|
|
158
|
+
EP1["METHOD /path"]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
subgraph ACTIVITIES["Activities Exposed"]
|
|
162
|
+
ACT1["⚡ ActivityName"]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
subgraph USE_CASES["Use Cases"]
|
|
166
|
+
UC1["UseCaseName"]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
subgraph EVENTS_OUT["Events → Workflows"]
|
|
170
|
+
EO1["📤 EventName → WorkflowName"]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
EP1 --> UC1
|
|
174
|
+
UC1 --> EO1
|
|
175
|
+
ACT1 -->|"invoked by WorkflowName"| IMPL["Internal logic"]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
> Node conventions:
|
|
179
|
+
> - HTTP endpoints → rectangles with method and path
|
|
180
|
+
> - Activities → `⚡` prefix with activity name
|
|
181
|
+
> - Use cases → rectangles with handler name
|
|
182
|
+
> - Events with workflows → `📤` prefix + event → workflow
|
|
183
|
+
> - Omit sections if not applicable
|
|
184
|
+
|
|
185
|
+
## Sequence Diagram
|
|
186
|
+
[Chronological interactions for main flows]
|
|
187
|
+
|
|
188
|
+
```mermaid
|
|
189
|
+
sequenceDiagram
|
|
190
|
+
actor Client as Client
|
|
191
|
+
participant API as REST API
|
|
192
|
+
participant Handler as CommandHandler
|
|
193
|
+
participant Domain as Domain Entity
|
|
194
|
+
participant Repo as Repository
|
|
195
|
+
participant Temporal as Temporal Server
|
|
196
|
+
|
|
197
|
+
Client->>API: POST /resource {payload}
|
|
198
|
+
API->>Handler: CreateResourceCommand
|
|
199
|
+
Handler->>Domain: new Resource(...)
|
|
200
|
+
Domain-->>Handler: entity with DomainEvent
|
|
201
|
+
Handler->>Repo: save(entity)
|
|
202
|
+
Note over Repo,Temporal: DomainEventHandler intercepts event
|
|
203
|
+
Repo->>Temporal: startWorkflow(ResourceCreatedWorkflow)
|
|
204
|
+
Handler-->>API: resourceId
|
|
205
|
+
API-->>Client: 201 Created {resourceId}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
> **Key constraint:** The handler above does NOT call any activity. It persists the entity, and the DomainEventHandler starts the workflow. Activities are ONLY invoked from within workflows (see activity invocation diagram below).
|
|
209
|
+
|
|
210
|
+
> For activity invocations:
|
|
211
|
+
|
|
212
|
+
```mermaid
|
|
213
|
+
sequenceDiagram
|
|
214
|
+
participant Temporal as Temporal Server
|
|
215
|
+
participant Worker as Module Worker
|
|
216
|
+
participant Activity as ActivityImpl
|
|
217
|
+
participant Repo as Repository
|
|
218
|
+
|
|
219
|
+
Note over Temporal,Repo: Activities are ONLY invoked from workflows via Temporal stubs — never from handlers
|
|
220
|
+
Temporal->>Worker: schedule ActivityName
|
|
221
|
+
Worker->>Activity: execute(input)
|
|
222
|
+
Activity->>Repo: findById(id)
|
|
223
|
+
Repo-->>Activity: entity
|
|
224
|
+
Activity->>Activity: businessLogic()
|
|
225
|
+
Activity->>Repo: save(entity)
|
|
226
|
+
Activity-->>Worker: result
|
|
227
|
+
Worker-->>Temporal: complete(result)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Use Cases
|
|
231
|
+
[One `###` per useCase in exposes:]
|
|
232
|
+
|
|
233
|
+
### {UseCaseName}
|
|
234
|
+
**Type:** `HTTP`
|
|
235
|
+
**What it does:** [detailed business logic description]
|
|
236
|
+
**Preconditions:** [valid states, entities that must exist]
|
|
237
|
+
**Postconditions:** [system state after successful execution]
|
|
238
|
+
**Invariants verified:** [ID list]
|
|
239
|
+
**Validations and errors:** [exception conditions]
|
|
240
|
+
**Events emitted:** [{EventName} → triggers {WorkflowName}, or "none"]
|
|
241
|
+
**Operations:**
|
|
242
|
+
1. [Load entity by id via {Repository}.findById() — throw NotFoundException if absent]
|
|
243
|
+
2. [Call {PortName}.{method}() to obtain cross-module data — if applicable]
|
|
244
|
+
3. [Invoke entity.{domainMethod}() — domain enforces invariants]
|
|
245
|
+
4. [Persist via {Repository}.save(entity)]
|
|
246
|
+
5. [DomainEventHandler publishes event → Temporal starts {WorkflowName} — if applicable]
|
|
247
|
+
|
|
248
|
+
## Activities Exposed
|
|
249
|
+
[One `###` per activity in activities:]
|
|
250
|
+
|
|
251
|
+
### {ActivityName}
|
|
252
|
+
**Type:** `light` | `heavy`
|
|
253
|
+
**Invoked by:** [{WorkflowName} from system.yaml or internal workflow]
|
|
254
|
+
**Input:** [fields with types and descriptions]
|
|
255
|
+
**Output:** [fields with types, or "void"]
|
|
256
|
+
**Compensation:** [{compensation activity} or "none — irreversible"]
|
|
257
|
+
**What it does:** [detailed business logic]
|
|
258
|
+
**Invariants verified:** [ID list]
|
|
259
|
+
**Operations:**
|
|
260
|
+
1. [Load entity by id via {Repository}.findById() — throw ApplicationFailure if absent (workflow handles)]
|
|
261
|
+
2. [Call {PortName}.{method}() — if applicable]
|
|
262
|
+
3. [Invoke entity.{domainMethod}() — domain enforces invariants]
|
|
263
|
+
4. [Persist via {Repository}.save(entity)]
|
|
264
|
+
5. [Return {OutputType} to workflow]
|
|
265
|
+
**Data access:** [repositories and operations performed]
|
|
266
|
+
|
|
267
|
+
**Flow diagram:**
|
|
268
|
+
```mermaid
|
|
269
|
+
flowchart TD
|
|
270
|
+
IN["⚡ ActivityName invoked by WorkflowName"] --> CHECK{"Validate invariants"}
|
|
271
|
+
CHECK -->|"violated"| ERR[/"Exception → workflow handles"/]
|
|
272
|
+
CHECK -->|"passed"| BIZ["Execute business logic"]
|
|
273
|
+
BIZ --> SAVE["Persist changes"]
|
|
274
|
+
SAVE --> OUT["Return result to workflow"]
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Exposed Endpoints
|
|
278
|
+
[One `###` per endpoint]
|
|
279
|
+
|
|
280
|
+
### {METHOD} {/path}
|
|
281
|
+
**Use case:** `{UseCase}`
|
|
282
|
+
**Purpose:** [description]
|
|
283
|
+
**Path params:** _(omit table if none)_
|
|
284
|
+
|
|
285
|
+
| Param | Type | Description |
|
|
286
|
+
|-------|------|-------------|
|
|
287
|
+
| {param} | `String` | [description] |
|
|
288
|
+
|
|
289
|
+
**Query params:** _(GET / DELETE only — omit table if none)_
|
|
290
|
+
|
|
291
|
+
| Param | Type | Required | Default | Description |
|
|
292
|
+
|-------|------|----------|---------|-------------|
|
|
293
|
+
| {param} | `String` | No | — | [description] |
|
|
294
|
+
|
|
295
|
+
**Request body:** _(POST / PUT / PATCH only — omit for GET / DELETE)_
|
|
296
|
+
```json
|
|
297
|
+
{
|
|
298
|
+
"field": "String", // required — [constraint]
|
|
299
|
+
"otherField": "Integer" // optional — [constraint]
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Response schema:** _(GET single only — omit for mutations)_
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"id": "String",
|
|
307
|
+
"field": "Type" // [business meaning]
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
_(For GET list: `{ "content": [...], "totalElements": "Long", "page": "Integer", "size": "Integer" }`)_
|
|
311
|
+
|
|
312
|
+
**Errors:**
|
|
313
|
+
|
|
314
|
+
| Status | Condition |
|
|
315
|
+
|--------|-----------|
|
|
316
|
+
| 404 | [entity] not found |
|
|
317
|
+
| 400 | [validation failure] |
|
|
318
|
+
| 409 | [invariant violated] |
|
|
319
|
+
|
|
320
|
+
## Single-Module Workflows
|
|
321
|
+
[Only if workflows: in domain.yaml]
|
|
322
|
+
|
|
323
|
+
### {WorkflowName}
|
|
324
|
+
**Trigger:** [internal event or condition]
|
|
325
|
+
**Task Queue:** [{QUEUE_NAME}]
|
|
326
|
+
**Purpose:** [why this internal workflow exists]
|
|
327
|
+
**Steps:**
|
|
328
|
+
1. [Step description]
|
|
329
|
+
2. [Step description]
|
|
330
|
+
|
|
331
|
+
## Ports (External Services)
|
|
332
|
+
[Only if ports: in domain.yaml]
|
|
333
|
+
|
|
334
|
+
### {PortName} → {target}
|
|
335
|
+
**When called:** [in which activity]
|
|
336
|
+
**Endpoints used:** [METHOD /path list]
|
|
337
|
+
**Data obtained and how it's used:** [description]
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
### Rules for module specifications
|
|
343
|
+
|
|
344
|
+
- **All in English**: titles, sections, descriptions, invariants, use cases.
|
|
345
|
+
- **MANDATORY invariants**: at least 2–3 per module. Analyze uniqueness, valid states, ranges, transition preconditions.
|
|
346
|
+
- **MANDATORY interaction diagram**: all endpoints, activities, and events.
|
|
347
|
+
- **MANDATORY sequence diagram**: at least one covering the happy path. Additional for branching (error, compensation).
|
|
348
|
+
- **Flow diagram per use case and activity**: `flowchart TD` with trigger, invariants, logic, and output.
|
|
349
|
+
- **State machine**: only if entities have lifecycle. Transition restrictions are implicit invariants.
|
|
350
|
+
- **Reference invariants** in each use case and activity (INV-01, INV-02...).
|
|
351
|
+
- **Endpoints with rich contracts**: for each endpoint, separate path params, query params, body, and response into distinct blocks. Use real domain field names and types — never generic placeholders. GET endpoints must include the JSON response schema; POST/PUT/PATCH must include the JSON body. List endpoints include pagination wrapper `{ content, totalElements, page, size }`.
|
|
352
|
+
- **Operations in use cases and activities**: the `**Operations:**` field is **mandatory** in each `### {UseCase}` and `### {ActivityName}`. Lists the handler/activity steps with concrete names: repository, port, domain method, event, and workflow. Acts as an evaluable implementation specification for human reviewers before code generation.
|
|
353
|
+
- Files in `system/{module-name}.md`.
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# Temporal system.yaml — Complete Specification
|
|
2
|
+
|
|
3
|
+
Reference for building and validating `system/system.yaml` in a Temporal-based system.
|
|
4
|
+
|
|
5
|
+
> **PATH RULE:** The file MUST be saved as `system/system.yaml` — inside the `system/` directory at the project root. NEVER save as `system.yaml` at the project root.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Complete structure
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
system:
|
|
13
|
+
name: project-name # kebab-case
|
|
14
|
+
groupId: com.example
|
|
15
|
+
javaVersion: 21
|
|
16
|
+
springBootVersion: 3.5.5
|
|
17
|
+
database: postgresql # h2 | postgresql | mysql
|
|
18
|
+
|
|
19
|
+
# ─── Temporal replaces both messaging (Kafka) and sync (Feign) ───────────────
|
|
20
|
+
orchestration:
|
|
21
|
+
enabled: true
|
|
22
|
+
engine: temporal
|
|
23
|
+
temporal:
|
|
24
|
+
target: localhost:7233
|
|
25
|
+
namespace: project-name
|
|
26
|
+
# Task queues are module-prefixed (module-scoped):
|
|
27
|
+
# {MODULE}_WORKFLOW_QUEUE — Workflows of the module
|
|
28
|
+
# {MODULE}_LIGHT_TASK_QUEUE — Fast activities (< 30s)
|
|
29
|
+
# {MODULE}_HEAVY_TASK_QUEUE — Heavy activities (up to 2min)
|
|
30
|
+
|
|
31
|
+
modules:
|
|
32
|
+
- name: orders # plural, kebab-case
|
|
33
|
+
description: "Order lifecycle management"
|
|
34
|
+
exposes:
|
|
35
|
+
- method: GET # GET | POST | PUT | PATCH | DELETE
|
|
36
|
+
path: /orders/{id}
|
|
37
|
+
useCase: GetOrder
|
|
38
|
+
description: "Get order by ID"
|
|
39
|
+
- method: POST
|
|
40
|
+
path: /orders
|
|
41
|
+
useCase: CreateOrder
|
|
42
|
+
description: "Create a new order"
|
|
43
|
+
|
|
44
|
+
- name: notifications
|
|
45
|
+
description: "Notification delivery service"
|
|
46
|
+
# No REST endpoints — only exposes activities
|
|
47
|
+
|
|
48
|
+
# ─── WORKFLOWS: Cross-module business flows orchestrated by Temporal ─────────
|
|
49
|
+
# Only REAL business flows — no data-sync workflows.
|
|
50
|
+
# Cross-module data is obtained on-demand with Remote Activities (reads).
|
|
51
|
+
workflows:
|
|
52
|
+
|
|
53
|
+
- name: PlaceOrderWorkflow
|
|
54
|
+
trigger:
|
|
55
|
+
module: orders # Module that triggers this workflow
|
|
56
|
+
on: create # Business event: create | cancel | confirm | etc.
|
|
57
|
+
taskQueue: ORDER_WORKFLOW_QUEUE
|
|
58
|
+
saga: true # Enables compensation on failure
|
|
59
|
+
steps:
|
|
60
|
+
- activity: GetOrderDetails
|
|
61
|
+
target: orders # Module that owns this activity
|
|
62
|
+
type: sync # sync = wait for result | async = fire-and-forget
|
|
63
|
+
input: [orderId]
|
|
64
|
+
output: [customerId, items, totalAmount]
|
|
65
|
+
timeout: 5s
|
|
66
|
+
|
|
67
|
+
- activity: GetCustomerById
|
|
68
|
+
target: customers
|
|
69
|
+
type: sync
|
|
70
|
+
input: [customerId]
|
|
71
|
+
output: [customerId, firstName, lastName, email]
|
|
72
|
+
timeout: 5s
|
|
73
|
+
|
|
74
|
+
- activity: ReserveStock
|
|
75
|
+
target: inventory
|
|
76
|
+
type: sync
|
|
77
|
+
input: [orderId, items]
|
|
78
|
+
compensation: ReleaseStock # Activity to call on saga rollback
|
|
79
|
+
timeout: 10s
|
|
80
|
+
|
|
81
|
+
- activity: ProcessOrderPayment
|
|
82
|
+
target: payments
|
|
83
|
+
type: sync
|
|
84
|
+
input: [orderId, customerId, totalAmount]
|
|
85
|
+
output: [paymentId]
|
|
86
|
+
compensation: RefundPayment
|
|
87
|
+
timeout: 30s
|
|
88
|
+
|
|
89
|
+
- activity: ConfirmOrder
|
|
90
|
+
target: orders
|
|
91
|
+
type: sync
|
|
92
|
+
input: [orderId]
|
|
93
|
+
|
|
94
|
+
- activity: NotifyOrderPlaced
|
|
95
|
+
target: notifications
|
|
96
|
+
type: async # Fire-and-forget — does NOT block saga
|
|
97
|
+
input: [orderId, email, firstName, totalAmount]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Sections NOT present in Temporal system.yaml
|
|
103
|
+
|
|
104
|
+
These sections from broker-based systems are **replaced** by Temporal:
|
|
105
|
+
|
|
106
|
+
| Absent section | Replacement |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `messaging:` | `orchestration:` |
|
|
109
|
+
| `integrations.async:` | `workflows:` with cross-module steps |
|
|
110
|
+
| `integrations.sync:` | `workflows:` with Remote Activity steps |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Naming conventions
|
|
115
|
+
|
|
116
|
+
| Element | Convention | Valid example | Invalid example |
|
|
117
|
+
|---|---|---|---|
|
|
118
|
+
| Modules | plural, kebab-case | `orders`, `order-items` | `Order`, `order_items` |
|
|
119
|
+
| Workflows | PascalCase + `Workflow` | `PlaceOrderWorkflow` | `placeOrderWorkflow` |
|
|
120
|
+
| Activities | PascalCase, verb + noun | `ReserveStock`, `GetCustomerById` | `reserveStock` |
|
|
121
|
+
| Task Queues | SCREAMING_SNAKE_CASE | `ORDER_WORKFLOW_QUEUE` | `order-queue` |
|
|
122
|
+
| Events (in domain.yaml) | PascalCase + past + `Event` | `OrderPlacedEvent` | `PlaceOrderEvent` |
|
|
123
|
+
| useCases | PascalCase, verb + noun | `CreateOrder`, `ConfirmOrder` | `createOrder` |
|
|
124
|
+
|
|
125
|
+
### Task Queue naming
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
{MODULE_SCREAMING_SNAKE}_WORKFLOW_QUEUE → ORDER_WORKFLOW_QUEUE
|
|
129
|
+
{MODULE_SCREAMING_SNAKE}_LIGHT_TASK_QUEUE → CUSTOMER_LIGHT_TASK_QUEUE
|
|
130
|
+
{MODULE_SCREAMING_SNAKE}_HEAVY_TASK_QUEUE → PAYMENT_HEAVY_TASK_QUEUE
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Activity naming
|
|
134
|
+
|
|
135
|
+
| Type | Pattern | Example |
|
|
136
|
+
|------|---------|---------|
|
|
137
|
+
| Read singular | `Get{Entity}ById` | `GetCustomerById` |
|
|
138
|
+
| Read batch | `Get{Entities}ByIds` | `GetProductsByIds` |
|
|
139
|
+
| Write | `{Verb}{Noun}` | `ReserveStock`, `ProcessOrderPayment` |
|
|
140
|
+
| Compensation | `{InverseVerb}{Noun}` | `ReleaseStock`, `RefundPayment` |
|
|
141
|
+
| Reactor | `Notify{Event}` | `NotifyOrderPlaced` |
|
|
142
|
+
| Local | `{Verb}{Noun}` | `ConfirmOrder`, `RetryCharge` |
|
|
143
|
+
|
|
144
|
+
### Workflow naming
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
{Verb}{Entity}Workflow → PlaceOrderWorkflow
|
|
148
|
+
{Entity}{Event}Workflow → ProductCreatedWorkflow
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Workflow step properties
|
|
154
|
+
|
|
155
|
+
| Property | Required | Description |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| `activity` | ✅ | Name of the activity to invoke |
|
|
158
|
+
| `target` | ✅ | Module that owns the activity |
|
|
159
|
+
| `type` | ✅ | `sync` (wait for result) or `async` (fire-and-forget) |
|
|
160
|
+
| `input` | ✅ | List of input field names |
|
|
161
|
+
| `output` | ❌ | List of output field names (for sync steps that return data) |
|
|
162
|
+
| `compensation` | ❌ | Activity name to call on saga rollback |
|
|
163
|
+
| `timeout` | ❌ | Step timeout (e.g., `10s`, `30s`, `2m`) |
|
|
164
|
+
| `optional` | ❌ | `true` if the step can fail without failing the saga |
|
|
165
|
+
| `parallel` | ❌ | `true` to execute in parallel with adjacent parallel steps |
|
|
166
|
+
|
|
167
|
+
### Parallel steps
|
|
168
|
+
|
|
169
|
+
Steps marked `parallel: true` are executed concurrently using `Async.function()`:
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
steps:
|
|
173
|
+
- activity: GetProductsByIds
|
|
174
|
+
target: products
|
|
175
|
+
type: sync
|
|
176
|
+
parallel: true # ⎫ executed in parallel
|
|
177
|
+
- activity: ReserveStock # ⎭
|
|
178
|
+
target: inventory
|
|
179
|
+
type: sync
|
|
180
|
+
parallel: true
|
|
181
|
+
- activity: ProcessOrderPayment # after both parallel steps
|
|
182
|
+
target: payments
|
|
183
|
+
type: sync
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Structural restrictions
|
|
189
|
+
|
|
190
|
+
- ❌ **No `messaging:` section** — Temporal replaces Kafka/RabbitMQ
|
|
191
|
+
- ❌ **No `integrations:` section** — replaced by `workflows:`
|
|
192
|
+
- ❌ **No data-sync workflows** — use on-demand reads via Activities
|
|
193
|
+
- ❌ **No activities that do cross-module lookups** — workflow assembles data
|
|
194
|
+
- ✅ Each workflow step `target` must reference an existing module in `modules:`
|
|
195
|
+
- ✅ `compensation:` activities must be declared in the target module's domain.yaml
|
|
196
|
+
- ✅ `saga: true` workflows should have `compensation:` on reversible steps
|
|
197
|
+
- ✅ `type: async` only for non-critical steps (notifications, analytics)
|
|
198
|
+
- ✅ `ports:` only for external services (not between internal modules)
|
|
199
|
+
- ✅ All content in English
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Workflow patterns
|
|
204
|
+
|
|
205
|
+
### Pattern 1: Saga with Compensation
|
|
206
|
+
|
|
207
|
+
Multi-step flow where failure of a step reverses the previous ones.
|
|
208
|
+
|
|
209
|
+
```yaml
|
|
210
|
+
workflows:
|
|
211
|
+
- name: PlaceOrderWorkflow
|
|
212
|
+
saga: true
|
|
213
|
+
steps:
|
|
214
|
+
- activity: ReserveStock
|
|
215
|
+
compensation: ReleaseStock
|
|
216
|
+
- activity: ProcessOrderPayment
|
|
217
|
+
compensation: RefundPayment
|
|
218
|
+
- activity: ConfirmOrder # last step — no compensation needed
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Pattern 2: Enrichment + Action
|
|
222
|
+
|
|
223
|
+
Obtain read data before executing the main action.
|
|
224
|
+
|
|
225
|
+
```yaml
|
|
226
|
+
steps:
|
|
227
|
+
- activity: GetCustomerById # read (enrichment)
|
|
228
|
+
output: [firstName, email]
|
|
229
|
+
- activity: ProcessOrderPayment # action (uses enrichment data)
|
|
230
|
+
input: [orderId, customerId, totalAmount]
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Pattern 3: Parallel Steps
|
|
234
|
+
|
|
235
|
+
Execute independent steps simultaneously.
|
|
236
|
+
|
|
237
|
+
```yaml
|
|
238
|
+
steps:
|
|
239
|
+
- activity: GetProductsByIds
|
|
240
|
+
parallel: true
|
|
241
|
+
- activity: ReserveStock
|
|
242
|
+
parallel: true
|
|
243
|
+
- activity: ProcessOrderPayment # after both complete
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Pattern 4: Single Business Effect
|
|
247
|
+
|
|
248
|
+
An event triggers a single action in another module.
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
workflows:
|
|
252
|
+
- name: ProductCreatedWorkflow
|
|
253
|
+
trigger:
|
|
254
|
+
module: products
|
|
255
|
+
on: create
|
|
256
|
+
steps:
|
|
257
|
+
- activity: InitializeStock
|
|
258
|
+
compensation: DeleteStock
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Pattern 5: Non-Blocking Notification
|
|
262
|
+
|
|
263
|
+
Last step, doesn't affect saga outcome.
|
|
264
|
+
|
|
265
|
+
```yaml
|
|
266
|
+
steps:
|
|
267
|
+
- activity: NotifyOrderPlaced
|
|
268
|
+
type: async # fire-and-forget
|
|
269
|
+
# NO compensation — failure doesn't reverse the saga
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## useCases — naming patterns
|
|
275
|
+
|
|
276
|
+
### Verbs by operation type
|
|
277
|
+
|
|
278
|
+
| Operation | Recommended verbs | Example |
|
|
279
|
+
|---|---|---|
|
|
280
|
+
| Create resource | `Create` | `CreateOrder` |
|
|
281
|
+
| Update | `Update` | `UpdateOrder` |
|
|
282
|
+
| Delete | `Delete` | `DeleteOrder` |
|
|
283
|
+
| Get by ID | `Get` | `GetOrder` |
|
|
284
|
+
| List with pagination | `FindAll` | `FindAllOrders` |
|
|
285
|
+
| State transition | `Confirm`, `Cancel`, `Approve` | `ConfirmOrder` |
|
|
286
|
+
| Punctual action | `Send`, `Process`, `Calculate` | `ProcessPayment` |
|
|
287
|
+
|
|
288
|
+
### CRUD standard — generates complete implementation:
|
|
289
|
+
|
|
290
|
+
| Pattern | HTTP | Implementation |
|
|
291
|
+
|---|---|---|
|
|
292
|
+
| `Create{Aggregate}` | POST `/resource` | Complete handler |
|
|
293
|
+
| `Update{Aggregate}` | PUT `/resource/{id}` | Complete handler |
|
|
294
|
+
| `Delete{Aggregate}` | DELETE `/resource/{id}` | Complete handler |
|
|
295
|
+
| `Get{Aggregate}` | GET `/resource/{id}` | Complete handler |
|
|
296
|
+
| `FindAll{PluralAggregate}` | GET `/resource` | Complete handler |
|
|
297
|
+
|
|
298
|
+
### Business useCases — generates scaffold:
|
|
299
|
+
|
|
300
|
+
```java
|
|
301
|
+
public class ConfirmOrderCommandHandler implements CommandHandler<ConfirmOrderCommand, Void> {
|
|
302
|
+
@Override
|
|
303
|
+
public Void handle(ConfirmOrderCommand command) {
|
|
304
|
+
throw new UnsupportedOperationException("ConfirmOrderCommandHandler not implemented yet");
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Validation checklist
|
|
312
|
+
|
|
313
|
+
- [ ] Modules in plural kebab-case
|
|
314
|
+
- [ ] `orchestration:` section present with `engine: temporal`
|
|
315
|
+
- [ ] No `messaging:` section
|
|
316
|
+
- [ ] No `integrations:` section
|
|
317
|
+
- [ ] All workflow steps have `activity:`, `target:`, `type:`
|
|
318
|
+
- [ ] All `target:` modules exist in `modules:`
|
|
319
|
+
- [ ] Saga workflows have `saga: true`
|
|
320
|
+
- [ ] Reversible steps in sagas have `compensation:`
|
|
321
|
+
- [ ] `type: async` only for non-critical steps
|
|
322
|
+
- [ ] No data-sync workflows
|
|
323
|
+
- [ ] Task queues follow module-prefixed naming
|
|
324
|
+
- [ ] useCases in PascalCase
|
|
325
|
+
- [ ] All content in English
|
|
326
|
+
- [ ] File saved inside `system/` directory as `system/system.yaml` (NEVER at the project root)
|