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,752 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: build-temporal-system
|
|
3
|
+
description: 'Build or update a Temporal-based system with eva4j. Use when designing a system architecture that uses Temporal as the orchestration engine for cross-module communication instead of Kafka/RabbitMQ. Invoke this skill when the user mentions Temporal workflows, sagas, durable orchestration, activity-based communication between modules, or wants to design a system where Temporal replaces both async messaging and sync HTTP calls between internal modules. USE FOR: Temporal system architecture design, designing cross-module workflows and sagas, defining activities per module, building system.yaml with orchestration section, generating domain YAMLs with activities/workflows/notifies, C4 diagrams for Temporal systems. DO NOT USE FOR: Kafka/RabbitMQ-based systems (use build-system-yaml instead), single-module projects without cross-module communication, Temporal worker implementation details.'
|
|
4
|
+
argument-hint: 'Describe the system or modules that need Temporal orchestration (e.g., e-commerce with orders, payments, inventory, and notifications orchestrated by Temporal)'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Build Temporal System
|
|
8
|
+
|
|
9
|
+
You play two simultaneous roles:
|
|
10
|
+
|
|
11
|
+
1. **Software Architect** expert in DDD, hexagonal architecture, and Temporal workflow orchestration. You decide how to structure the system into modules, what patterns to apply, and how bounded contexts communicate via Temporal activities and workflows.
|
|
12
|
+
|
|
13
|
+
2. **Business domain expert** — the domain is defined by the user in the chat. You reason as someone who deeply understands the business rules, processes, and constraints: you know which operations make sense, which flows are mandatory, which invariants must never be violated, and how actors interact with the system.
|
|
14
|
+
|
|
15
|
+
> **Clarity principle:** when you detect ambiguities that could affect design decisions, **ask the user** before continuing. Never more than 3–5 questions at a time. Only genuinely functional questions.
|
|
16
|
+
|
|
17
|
+
## Project discovery
|
|
18
|
+
|
|
19
|
+
Before generating anything, read these project files for context:
|
|
20
|
+
- `system/system.yaml` — if it exists, the project already has architecture defined; read it first
|
|
21
|
+
- `package.json` or project configuration for name, groupId, versions
|
|
22
|
+
- [AGENTS.md](/AGENTS.md) — eva4j patterns and conventions
|
|
23
|
+
|
|
24
|
+
## Language of generated files
|
|
25
|
+
|
|
26
|
+
> **ABSOLUTE RULE — ALWAYS IN ENGLISH:**
|
|
27
|
+
> All content in `.yaml` and `.md` files must be in English: module names, descriptions, comments, titles, invariants, narrative text. The conversation can be in any language; the files, always in English.
|
|
28
|
+
|
|
29
|
+
## When to use this skill
|
|
30
|
+
|
|
31
|
+
- Design the initial architecture of a new system using Temporal
|
|
32
|
+
- Add modules to an existing Temporal-based project
|
|
33
|
+
- Define cross-module workflows (sagas, orchestrated processes)
|
|
34
|
+
- Define activities that modules expose for inter-module communication
|
|
35
|
+
- Define single-module internal workflows (retries, timeouts, scheduling)
|
|
36
|
+
- Review or refactor module structure in a Temporal system
|
|
37
|
+
- Generate C4 diagrams for Temporal systems
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Core Architecture: Temporal as Orchestration Engine
|
|
42
|
+
|
|
43
|
+
Temporal replaces **both** Kafka (async messaging) and Feign (sync HTTP) for inter-module communication. The key changes from a broker-based system:
|
|
44
|
+
|
|
45
|
+
| Concept | Kafka/RabbitMQ System | Temporal System |
|
|
46
|
+
|---------|----------------------|-----------------|
|
|
47
|
+
| Cross-module communication | Events (async) + Feign (sync) | Remote Activities + Child Workflows |
|
|
48
|
+
| Data synchronization | Read Models (local projections) | On-demand reads via Activities |
|
|
49
|
+
| Saga / compensation | Manual event choreography | Durable Saga with `compensation:` |
|
|
50
|
+
| Fire-and-forget | Publish event + consumer group | `type: async` activity (Async.function) |
|
|
51
|
+
| Retry with backoff | Consumer retry config | Activity `retryPolicy:` |
|
|
52
|
+
| Timeout handling | TTL + dead letter queue | `Workflow.await()` + timeout |
|
|
53
|
+
|
|
54
|
+
### Key Principles
|
|
55
|
+
|
|
56
|
+
1. **No Read Models** — data from other modules is fetched on-demand via Remote Activities of type read (`GetCustomerById`, `GetProductsByIds`). No local projections needed.
|
|
57
|
+
2. **No listeners/consumers** — modules don't subscribe to events. Workflows invoke activities directly.
|
|
58
|
+
3. **No Feign/HTTP between modules** — `ports:` is only for **external** services (payment gateways, email providers, third-party APIs).
|
|
59
|
+
4. **Events use `notifies:`** — Domain Events reference the workflow they trigger, not a topic.
|
|
60
|
+
5. **Activities = module capabilities** — each module declares what it can do. Workflows compose these capabilities.
|
|
61
|
+
6. **Activities are workflow-only** — handlers, use cases, and REST controllers NEVER invoke activities directly. If a handler needs cross-module data, it emits a Domain Event → triggers a workflow → workflow invokes the required activities. Even local activities are invoked from single-module workflows in `domain.yaml`, not from handlers.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Workflow — complete step sequence
|
|
66
|
+
|
|
67
|
+
All generated files go inside the `system/` directory at the project root. **NEVER** place files at the project root level.
|
|
68
|
+
|
|
69
|
+
| Step | Generated file (inside `system/` dir) | Reference |
|
|
70
|
+
|------|----------------------------------------|-----------||
|
|
71
|
+
| 1 | _(information gathering)_ | This file |
|
|
72
|
+
| 2–5 | `system/system.yaml` | This file + `references/temporal-system-yaml-spec.md` |
|
|
73
|
+
| 6 | `system/system.md` | `references/temporal-module-spec.md` (system.md section) |
|
|
74
|
+
| 6.5 | `system/c4-context.mmd` + `system/c4-container.mmd` | This file (C4 section) |
|
|
75
|
+
| 7 | `system/{module}.yaml` (one per module) | `references/temporal-domain-yaml-spec.md` |
|
|
76
|
+
| 8 | `system/{module}.md` (one per module) | `references/temporal-module-spec.md` |
|
|
77
|
+
| 9a | `AGENTS.md` (project root — rewrite) | This file (Step 9) |
|
|
78
|
+
| 9b | `system/VALIDATION_FLOWS.md` | This file (Step 9) |
|
|
79
|
+
| 9c | `system/USER_FLOWS.md` | This file (Step 9) |
|
|
80
|
+
|
|
81
|
+
Execute **all** steps in order before returning control to the user.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Step 1 — Gather information
|
|
86
|
+
|
|
87
|
+
If the user didn't provide all the data, **ask** before generating:
|
|
88
|
+
|
|
89
|
+
0. **Business context** — What is the domain? Actors, key processes, important rules.
|
|
90
|
+
1. **List of modules** with their responsibility (plural, kebab-case)
|
|
91
|
+
2. **Endpoints REST** per module (method + path + use case)
|
|
92
|
+
3. **Cross-module flows**: What business events trigger multi-module processes? Which modules participate?
|
|
93
|
+
4. **Internal module flows**: Retries, timeouts, scheduling, verification flows?
|
|
94
|
+
5. **External services**: Payment gateways, email providers, third-party APIs?
|
|
95
|
+
6. **Temporal configuration**: Namespace, target address?
|
|
96
|
+
|
|
97
|
+
> If `system/system.yaml` already exists, read it and ask only about changes.
|
|
98
|
+
|
|
99
|
+
Apply the functional role: suggest necessary modules not mentioned, propose coherent workflows, anticipate invariants. Confirm before adding unsolicited elements.
|
|
100
|
+
|
|
101
|
+
### Decision Matrix: Workflow vs Domain Event
|
|
102
|
+
|
|
103
|
+
For each business event, ask: **Does something MUST happen in ANOTHER module when this occurs?**
|
|
104
|
+
|
|
105
|
+
| Answer | Result |
|
|
106
|
+
|--------|--------|
|
|
107
|
+
| YES — multi-step process, needs consistency | **Cross-module Workflow** (in system.yaml) |
|
|
108
|
+
| YES — single effect in one other module | **Simple Workflow** with 1 step (in system.yaml) |
|
|
109
|
+
| NO — internal module process needing durability | **Single-module Workflow** (in domain.yaml) |
|
|
110
|
+
| NO — nothing external reacts | **Domain Event internal** (no `notifies:`) |
|
|
111
|
+
|
|
112
|
+
> **⚠️ CRITICAL:** If a handler needs data from another module (e.g., cart handler needs product prices), do NOT invoke the activity directly from the handler. Instead, design a workflow that orchestrates the read activity (e.g., `GetProductsByIds`) and passes the result to subsequent steps. Activities are ONLY invoked from workflows.
|
|
113
|
+
|
|
114
|
+
### Decision: Activity Type
|
|
115
|
+
|
|
116
|
+
| Question | Type |
|
|
117
|
+
|----------|------|
|
|
118
|
+
| Only reads data from this module? | **Read** (`GetXById`, `GetXsByIds`) |
|
|
119
|
+
| Modifies data and can be undone? | **Write** + `compensation:` |
|
|
120
|
+
| Modifies data irreversibly? | **Write** without compensation |
|
|
121
|
+
| Is the reverse of another activity? | **Compensation** (referenced in `compensation:`) |
|
|
122
|
+
| Is a non-critical side effect? | **Reactor** (invoked as `type: async`) |
|
|
123
|
+
| Is an internal module operation? | **Local** (invoked by module's own workflows) |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Step 2 — Structure of system.yaml
|
|
128
|
+
|
|
129
|
+
Read `references/temporal-system-yaml-spec.md` for the complete structure, naming conventions, structural restrictions, and useCase patterns.
|
|
130
|
+
|
|
131
|
+
**Key structure:**
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
system:
|
|
135
|
+
name: project-name
|
|
136
|
+
groupId: com.example
|
|
137
|
+
javaVersion: 21
|
|
138
|
+
springBootVersion: 3.5.5
|
|
139
|
+
database: postgresql
|
|
140
|
+
|
|
141
|
+
orchestration:
|
|
142
|
+
enabled: true
|
|
143
|
+
engine: temporal
|
|
144
|
+
temporal:
|
|
145
|
+
target: localhost:7233
|
|
146
|
+
namespace: project-name
|
|
147
|
+
|
|
148
|
+
modules:
|
|
149
|
+
- name: orders
|
|
150
|
+
description: "Order lifecycle management"
|
|
151
|
+
exposes:
|
|
152
|
+
- method: POST
|
|
153
|
+
path: /orders
|
|
154
|
+
useCase: CreateOrder
|
|
155
|
+
|
|
156
|
+
workflows:
|
|
157
|
+
- name: PlaceOrderWorkflow
|
|
158
|
+
trigger:
|
|
159
|
+
module: orders
|
|
160
|
+
on: create
|
|
161
|
+
taskQueue: ORDER_WORKFLOW_QUEUE
|
|
162
|
+
saga: true
|
|
163
|
+
steps:
|
|
164
|
+
- activity: ReserveStock
|
|
165
|
+
target: inventory
|
|
166
|
+
type: sync
|
|
167
|
+
input: [orderId, items]
|
|
168
|
+
compensation: ReleaseStock
|
|
169
|
+
timeout: 10s
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Step 3 — Mandatory rules (summary)
|
|
175
|
+
|
|
176
|
+
| Element | Convention | Example |
|
|
177
|
+
|---------|-----------|---------|
|
|
178
|
+
| Modules | plural, kebab-case | `orders`, `product-catalog` |
|
|
179
|
+
| Workflows | PascalCase + `Workflow` | `PlaceOrderWorkflow` |
|
|
180
|
+
| Activities | PascalCase, verb+noun | `ReserveStock`, `GetCustomerById` |
|
|
181
|
+
| Task Queues | SCREAMING_SNAKE + suffix | `ORDER_WORKFLOW_QUEUE` |
|
|
182
|
+
| Events | PascalCase + past + `Event` | `OrderPlacedEvent` |
|
|
183
|
+
| useCases | PascalCase, verb+noun | `CreateOrder`, `ConfirmOrder` |
|
|
184
|
+
|
|
185
|
+
**Critical restrictions:**
|
|
186
|
+
- Cross-module workflows go in `system.yaml`, single-module workflows go in `{domain}.yaml`
|
|
187
|
+
- Activities declare what the module **can do**, workflows compose them
|
|
188
|
+
- `ports:` only for **external** services (non-Temporal)
|
|
189
|
+
- No `listeners:`, no `readModels:`, no `messaging:` section
|
|
190
|
+
- Events use `notifies:` to reference workflows, not topics
|
|
191
|
+
- All activities accessed ONLY their own module's data
|
|
192
|
+
- Activities are invoked ONLY from workflows (`system.yaml` or `domain.yaml`). Handlers and use cases do NOT have access to activity interfaces
|
|
193
|
+
- Compensation must be explicitly declared for reversible steps in sagas
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Step 4 — Validation checklist
|
|
198
|
+
|
|
199
|
+
Before proposing the `system.yaml`, verify:
|
|
200
|
+
|
|
201
|
+
- [ ] Modules in plural kebab-case
|
|
202
|
+
- [ ] `orchestration:` section with `engine: temporal`
|
|
203
|
+
- [ ] No `messaging:` section (Temporal replaces it)
|
|
204
|
+
- [ ] No `integrations:` section (Temporal replaces async and sync)
|
|
205
|
+
- [ ] Each workflow has `trigger:` with `module:` and `on:`
|
|
206
|
+
- [ ] Each workflow step has `activity:`, `target:`, `type:`, `input:`
|
|
207
|
+
- [ ] Saga workflows have `saga: true` and steps with `compensation:` where needed
|
|
208
|
+
- [ ] Activities with `type: async` for non-critical steps (fire-and-forget)
|
|
209
|
+
- [ ] Activities with `type: sync` for steps that need results
|
|
210
|
+
- [ ] Task queues follow module-prefixed naming
|
|
211
|
+
- [ ] No workflows that only sync data — use on-demand reads instead
|
|
212
|
+
- [ ] No handler or use case directly invokes an activity — all activity calls go through workflows
|
|
213
|
+
- [ ] All in English
|
|
214
|
+
- [ ] File in `system/system.yaml`
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Step 5 — Present and continue
|
|
219
|
+
|
|
220
|
+
> **CRITICAL PATH RULE:** ALL generated files live inside the `system/` directory at the project root.
|
|
221
|
+
> NEVER create `system.yaml` at the project root. The correct path is ALWAYS `system/system.yaml`.
|
|
222
|
+
> Same for module files: `system/{module}.yaml`, `system/{module}.md`, `system/system.md`, etc.
|
|
223
|
+
|
|
224
|
+
1. Create the `system/` directory at the project root if it doesn't exist
|
|
225
|
+
2. Save the file as `system/system.yaml` (INSIDE the `system/` directory, NOT at the project root)
|
|
226
|
+
3. Show the complete YAML
|
|
227
|
+
4. Explain non-obvious decisions
|
|
228
|
+
5. Mention warnings (coupling, diffuse responsibilities)
|
|
229
|
+
6. Proceed immediately to steps 6 → 6.5 → 7 → 8
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Step 6 — Create system.md
|
|
234
|
+
|
|
235
|
+
Read `references/temporal-module-spec.md` (section "system.md structure") for the mandatory structure.
|
|
236
|
+
|
|
237
|
+
Save as `system/system.md` (inside the `system/` directory). The `system/system.md` is the **narrative technical specification** of the system. One `##` section per module with: detailed role, use cases, endpoints, activities exposed, workflows triggered.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Step 6.5 — C4 Diagrams (Context + Container)
|
|
242
|
+
|
|
243
|
+
Immediately after `system.md`, generate **two Mermaid files** with C4 diagrams:
|
|
244
|
+
|
|
245
|
+
### `system/c4-context.mmd` — Context Diagram
|
|
246
|
+
|
|
247
|
+
Shows the system as a **single box** surrounded by actors and external systems.
|
|
248
|
+
|
|
249
|
+
```mermaid
|
|
250
|
+
C4Context
|
|
251
|
+
title System Context diagram for {System Name}
|
|
252
|
+
|
|
253
|
+
Person(user, "User", "Primary user who interacts with the system")
|
|
254
|
+
|
|
255
|
+
System(system, "{System Name}", "Core platform that manages {domain}")
|
|
256
|
+
|
|
257
|
+
System_Ext(paymentGw, "Payment Gateway", "Processes payments")
|
|
258
|
+
|
|
259
|
+
Rel(user, system, "Places orders", "HTTPS")
|
|
260
|
+
Rel(system, paymentGw, "Processes payments", "HTTPS")
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Rules:**
|
|
264
|
+
- System is **one node** — don't decompose into modules here
|
|
265
|
+
- `System_Ext()` only for truly external services (from `ports:`)
|
|
266
|
+
- Derive actors from who consumes `exposes[]`
|
|
267
|
+
|
|
268
|
+
### `system/c4-container.mmd` — Container Diagram
|
|
269
|
+
|
|
270
|
+
Decomposes the system into containers. Key difference from broker-based: **Temporal Server replaces the Message Broker**.
|
|
271
|
+
|
|
272
|
+
```mermaid
|
|
273
|
+
C4Container
|
|
274
|
+
title Container diagram for {System Name}
|
|
275
|
+
|
|
276
|
+
Person(user, "User", "Primary user of the system")
|
|
277
|
+
|
|
278
|
+
System_Boundary(boundary, "{System Name}") {
|
|
279
|
+
Container(orders, "orders", "Spring Boot Module", "Order lifecycle management")
|
|
280
|
+
Container(payments, "payments", "Spring Boot Module", "Payment processing")
|
|
281
|
+
|
|
282
|
+
ContainerDb(db, "Database", "PostgreSQL", "Stores all module data")
|
|
283
|
+
Container(temporal, "Temporal Server", "Temporal", "Durable workflow orchestration")
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
System_Ext(paymentGw, "Payment Gateway", "External payment processing")
|
|
287
|
+
|
|
288
|
+
Rel(user, orders, "Places orders", "REST/HTTPS")
|
|
289
|
+
|
|
290
|
+
Rel(orders, temporal, "Starts", "PlaceOrderWorkflow")
|
|
291
|
+
Rel(temporal, payments, "Invokes", "ProcessOrderPayment")
|
|
292
|
+
Rel(temporal, orders, "Invokes", "ConfirmOrder")
|
|
293
|
+
|
|
294
|
+
Rel(orders, db, "Reads/Writes", "JDBC")
|
|
295
|
+
Rel(payments, db, "Reads/Writes", "JDBC")
|
|
296
|
+
|
|
297
|
+
Rel(payments, paymentGw, "Charges", "HTTPS")
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Rules for Temporal Container diagram:**
|
|
301
|
+
- `Container(temporal, "Temporal Server", "Temporal", "Durable workflow orchestration")` — always inside the boundary
|
|
302
|
+
- Workflow arrows: `module → temporal` with "Starts" + workflow name
|
|
303
|
+
- Activity arrows: `temporal → module` with "Invokes" + activity name
|
|
304
|
+
- Direct arrows between modules are **forbidden** — all communication flows through Temporal
|
|
305
|
+
- `ports:` (external services) are direct arrows from module to `System_Ext()`
|
|
306
|
+
- No `ContainerQueue` — Temporal replaces the message broker
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Step 7 — Create domain.yaml per module
|
|
311
|
+
|
|
312
|
+
Read `references/temporal-domain-yaml-spec.md` for the complete specification.
|
|
313
|
+
|
|
314
|
+
For each module in `modules:`, generate `system/{module-name}.yaml` (**inside the `system/` directory**, e.g., `system/orders.yaml`, `system/payments.yaml`) with: aggregates, entities, valueObjects, enums (with transitions if applicable), events (with `notifies:` if applicable), activities, single-module workflows, endpoints, and ports (only for external services).
|
|
315
|
+
|
|
316
|
+
### Endpoints in multi-aggregate modules
|
|
317
|
+
|
|
318
|
+
If the module has **2 or more aggregates** (e.g., `Product` + `Category`), the `endpoints:` section must use `basePath: ""` (empty string) and **absolute** paths per operation:
|
|
319
|
+
|
|
320
|
+
```yaml
|
|
321
|
+
# Module with 2+ aggregates → empty basePath
|
|
322
|
+
endpoints:
|
|
323
|
+
basePath: ""
|
|
324
|
+
versions:
|
|
325
|
+
- version: v1
|
|
326
|
+
operations:
|
|
327
|
+
- useCase: CreateProduct
|
|
328
|
+
method: POST
|
|
329
|
+
path: /products
|
|
330
|
+
- useCase: CreateCategory
|
|
331
|
+
method: POST
|
|
332
|
+
path: /categories
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
If the module has **a single aggregate**, use `basePath: /resource` with relative paths (e.g., `/`, `/{id}`).
|
|
336
|
+
|
|
337
|
+
**NEVER use `basePath: /`** (with slash) — it produces a trailing slash in `@RequestMapping`. Use `basePath: ""` (empty).
|
|
338
|
+
|
|
339
|
+
### Key differences from broker-based domain.yaml
|
|
340
|
+
|
|
341
|
+
| Section | Broker-based | Temporal-based |
|
|
342
|
+
|---------|-------------|----------------|
|
|
343
|
+
| `events:` | `triggers:` / `lifecycle:` + `topic:` | `triggers:` / `lifecycle:` + `notifies:` (no topic) |
|
|
344
|
+
| `listeners:` | Present — Kafka consumers | **ABSENT** — Temporal replaces this |
|
|
345
|
+
| `readModels:` | Present — local projections | **ABSENT** — on-demand reads via activities |
|
|
346
|
+
| `ports:` | Internal + external HTTP calls | **Only external** services (non-Temporal) |
|
|
347
|
+
| `activities:` | Not present | **NEW** — module capabilities |
|
|
348
|
+
| `workflows:` | Not present | **NEW** — single-module internal flows |
|
|
349
|
+
|
|
350
|
+
### Activities section in domain.yaml
|
|
351
|
+
|
|
352
|
+
Each module declares its capabilities as activities:
|
|
353
|
+
|
|
354
|
+
```yaml
|
|
355
|
+
activities:
|
|
356
|
+
- name: GetCustomerById
|
|
357
|
+
type: light # light (<30s) | heavy (up to 2min)
|
|
358
|
+
description: "Gets a customer by ID"
|
|
359
|
+
input:
|
|
360
|
+
- name: customerId
|
|
361
|
+
type: String
|
|
362
|
+
output:
|
|
363
|
+
- name: customerId
|
|
364
|
+
type: String
|
|
365
|
+
- name: firstName
|
|
366
|
+
type: String
|
|
367
|
+
- name: email
|
|
368
|
+
type: String
|
|
369
|
+
timeout: 5s
|
|
370
|
+
|
|
371
|
+
- name: ReserveStock
|
|
372
|
+
type: light
|
|
373
|
+
description: "Reserves stock for an order"
|
|
374
|
+
input:
|
|
375
|
+
- name: orderId
|
|
376
|
+
type: String
|
|
377
|
+
- name: items
|
|
378
|
+
type: List<OrderItemDetail>
|
|
379
|
+
output:
|
|
380
|
+
- name: success
|
|
381
|
+
type: Boolean
|
|
382
|
+
externalTypes:
|
|
383
|
+
- name: OrderItemDetail
|
|
384
|
+
module: orders
|
|
385
|
+
timeout: 10s
|
|
386
|
+
compensation: ReleaseStock
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Workflows section in domain.yaml (single-module only)
|
|
390
|
+
|
|
391
|
+
```yaml
|
|
392
|
+
workflows:
|
|
393
|
+
- name: ExpireOrderWorkflow
|
|
394
|
+
description: "Cancels order if payment not received within timeout"
|
|
395
|
+
trigger:
|
|
396
|
+
on: orderCreated
|
|
397
|
+
taskQueue: ORDER_WORKFLOW_QUEUE
|
|
398
|
+
steps:
|
|
399
|
+
- wait: paymentCompleted
|
|
400
|
+
timeout: 30m
|
|
401
|
+
- activity: CancelExpiredOrder
|
|
402
|
+
timeout: 5s
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Events with `notifies:`
|
|
406
|
+
|
|
407
|
+
```yaml
|
|
408
|
+
events:
|
|
409
|
+
- name: OrderPlacedEvent
|
|
410
|
+
lifecycle: create
|
|
411
|
+
fields:
|
|
412
|
+
- name: orderId
|
|
413
|
+
type: String
|
|
414
|
+
notifies:
|
|
415
|
+
- workflow: PlaceOrderWorkflow # cross-module workflow in system.yaml
|
|
416
|
+
|
|
417
|
+
- name: CustomerUpdatedEvent
|
|
418
|
+
lifecycle: update
|
|
419
|
+
fields:
|
|
420
|
+
- name: customerId
|
|
421
|
+
type: String
|
|
422
|
+
# NO notifies → Domain Event internal (no cross-module effect)
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Step 8 — Create technical specification per module
|
|
428
|
+
|
|
429
|
+
Read `references/temporal-module-spec.md` for the mandatory structure.
|
|
430
|
+
|
|
431
|
+
For each module, generate `system/{module-name}.md` (**inside the `system/` directory**, e.g., `system/orders.md`, `system/payments.md`) with: module role, invariants, state machine, interaction diagram, sequence diagram, use cases, endpoints, activities exposed, and workflows.
|
|
432
|
+
|
|
433
|
+
### Key differences in module specs for Temporal
|
|
434
|
+
|
|
435
|
+
- **Activities Exposed** section replaces "Emitted Events" and "Ports"
|
|
436
|
+
- **Workflows Triggered** section describes what workflows each event triggers
|
|
437
|
+
- **Interaction diagrams** show workflow invocations instead of event flows
|
|
438
|
+
- **Sequence diagrams** show activity invocations through Temporal
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## Anti-patterns to avoid
|
|
443
|
+
|
|
444
|
+
### Do NOT create data-sync workflows
|
|
445
|
+
|
|
446
|
+
```yaml
|
|
447
|
+
# ❌ DON'T — workflow that only syncs data
|
|
448
|
+
- name: CustomerUpdatedWorkflow
|
|
449
|
+
steps:
|
|
450
|
+
- activity: SyncCustomerReadModel
|
|
451
|
+
|
|
452
|
+
# ✅ DO — on-demand read in the workflow that needs the data
|
|
453
|
+
# PlaceOrderWorkflow → GetCustomerById → customers
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Do NOT make activities do cross-module lookups
|
|
457
|
+
|
|
458
|
+
```yaml
|
|
459
|
+
# ❌ DON'T — activity internally queries another module's DB
|
|
460
|
+
activities:
|
|
461
|
+
- name: NotifyOrderPlaced
|
|
462
|
+
input: [orderId, customerId]
|
|
463
|
+
# Internally: fetch customer data → HIDDEN COUPLING
|
|
464
|
+
|
|
465
|
+
# ✅ DO — workflow assembles data and passes it
|
|
466
|
+
activities:
|
|
467
|
+
- name: NotifyOrderPlaced
|
|
468
|
+
input: [orderId, customerEmail, customerName, totalAmount]
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Do NOT put cross-module orchestration in domain.yaml
|
|
472
|
+
|
|
473
|
+
```yaml
|
|
474
|
+
# ❌ DON'T — saga in module YAML
|
|
475
|
+
# orders.yaml
|
|
476
|
+
saga:
|
|
477
|
+
workflow: PlaceOrderWorkflow
|
|
478
|
+
|
|
479
|
+
# ✅ DO — cross-module orchestration in system.yaml
|
|
480
|
+
# The domain.yaml only declares the event with notifies:
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Do NOT use `notifies:` for events without cross-module effects
|
|
484
|
+
|
|
485
|
+
```yaml
|
|
486
|
+
# ❌ DON'T
|
|
487
|
+
events:
|
|
488
|
+
- name: CustomerUpdatedEvent
|
|
489
|
+
notifies:
|
|
490
|
+
- workflow: CustomerUpdatedWorkflow # only syncs data
|
|
491
|
+
|
|
492
|
+
# ✅ DO — Domain Event internal
|
|
493
|
+
events:
|
|
494
|
+
- name: CustomerUpdatedEvent
|
|
495
|
+
# NO notifies
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Do NOT forget `compensation:` in saga activities
|
|
499
|
+
|
|
500
|
+
```yaml
|
|
501
|
+
# ❌ DON'T — no compensation in saga
|
|
502
|
+
steps:
|
|
503
|
+
- activity: ReserveStock # what if payment fails?
|
|
504
|
+
|
|
505
|
+
# ✅ DO
|
|
506
|
+
steps:
|
|
507
|
+
- activity: ReserveStock
|
|
508
|
+
compensation: ReleaseStock
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Do NOT invoke activities directly from handlers or use cases
|
|
512
|
+
|
|
513
|
+
Activities are Temporal constructs — they can ONLY be invoked from within a workflow execution context. Handlers and use cases interact with their own module's repository and domain entities, and trigger workflows via Domain Events. They never call activity interfaces.
|
|
514
|
+
|
|
515
|
+
```java
|
|
516
|
+
// ❌ DON'T — handler injecting an activity to get cross-module data
|
|
517
|
+
public class AddToCartCommandHandler {
|
|
518
|
+
private final ProductActivity productActivity; // ❌ WRONG
|
|
519
|
+
|
|
520
|
+
public void handle(AddToCartCommand cmd) {
|
|
521
|
+
// ❌ Activity invoked outside a workflow — will fail at runtime
|
|
522
|
+
var product = productActivity.GetProductById(cmd.productId());
|
|
523
|
+
cart.addItem(product.name(), product.price());
|
|
524
|
+
repository.save(cart);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// ✅ DO — handler persists + emits event → workflow orchestrates activities
|
|
529
|
+
public class AddToCartCommandHandler {
|
|
530
|
+
public void handle(AddToCartCommand cmd) {
|
|
531
|
+
cart.addItem(cmd.productId(), cmd.quantity());
|
|
532
|
+
repository.save(cart); // Domain Event triggers AddToCartWorkflow
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// ✅ Workflow invokes the activity through Temporal
|
|
537
|
+
public class AddToCartWorkflowImpl implements AddToCartWorkflow {
|
|
538
|
+
private final ProductActivity productActivity; // ✅ Temporal stub
|
|
539
|
+
|
|
540
|
+
public void execute(String cartId, String productId) {
|
|
541
|
+
var product = productActivity.GetProductById(productId); // ✅ CORRECT
|
|
542
|
+
var enrichment = cartActivity.EnrichCartItem(cartId, product);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
**Rule:** If a handler needs data from another module, design a workflow that reads the data via an activity and then acts on it. The handler's job is to persist domain state and emit events that trigger workflows.
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Step 9 — Post-design artifacts
|
|
552
|
+
|
|
553
|
+
Immediately after completing Step 8, generate three final artifacts that contextualize the newly designed system.
|
|
554
|
+
|
|
555
|
+
### Step 9a — Rewrite AGENTS.md (project-specific)
|
|
556
|
+
|
|
557
|
+
Rewrite the `AGENTS.md` file at the **project root** with content specific to the designed system.
|
|
558
|
+
|
|
559
|
+
**Process:**
|
|
560
|
+
|
|
561
|
+
1. Read the current `AGENTS.md` as a base template
|
|
562
|
+
2. Analyze `system/system.yaml` and all `system/{module}.yaml` to detect which features are used:
|
|
563
|
+
- Temporal orchestration (always true for this skill)
|
|
564
|
+
- `activities:` section with types (light/heavy, compensation)
|
|
565
|
+
- `workflows:` section (single-module internal workflows)
|
|
566
|
+
- Events with `notifies:` (cross-module workflow triggers)
|
|
567
|
+
- `ports:` (external services only — not internal HTTP)
|
|
568
|
+
- `hasSoftDelete` on any entity
|
|
569
|
+
- `audit.trackUser` on any entity
|
|
570
|
+
- Value Objects with `methods:`
|
|
571
|
+
- Enums with `transitions:` and `initialValue`
|
|
572
|
+
- Field flags: `readOnly`, `hidden`, `defaultValue`, `validations`, `reference`
|
|
573
|
+
3. **Prune** sections about unused features:
|
|
574
|
+
|
|
575
|
+
| Condition | Section to remove |
|
|
576
|
+
|---|---|
|
|
577
|
+
| Temporal system (always) | Kafka/RabbitMQ messaging sections, `listeners:`, `readModels:` |
|
|
578
|
+
| No `ports:` for external services | Ports/Feign subsections |
|
|
579
|
+
| No `hasSoftDelete` | Soft delete section and checklist items |
|
|
580
|
+
| No `audit.trackUser` | UserContextFilter/UserContextHolder/AuditorAwareImpl infrastructure (keep basic audit if `audit.enabled`) |
|
|
581
|
+
| No VO `methods:` | "Value Objects with Methods" subsection |
|
|
582
|
+
| No enum `transitions:` | "Enums with Lifecycle" subsection |
|
|
583
|
+
|
|
584
|
+
4. **Add Temporal-specific content** (always):
|
|
585
|
+
- Activities section: light/heavy types, compensation pattern, input/output
|
|
586
|
+
- Workflows section: cross-module (system.yaml) vs single-module (domain.yaml)
|
|
587
|
+
- `notifies:` pattern in events (replaces `topic:`)
|
|
588
|
+
- Activity as collaboration mechanism (replaces events/listeners)
|
|
589
|
+
5. **Specialize** remaining content:
|
|
590
|
+
- Replace generic examples (`User`, `Order`) with actual project entities/modules
|
|
591
|
+
- Update `eva` command examples with real module names
|
|
592
|
+
- Update `domain.yaml` example with actual project structure
|
|
593
|
+
- Reduce checklist to only relevant items for this project
|
|
594
|
+
6. Add a **project context header** at the top:
|
|
595
|
+
|
|
596
|
+
```markdown
|
|
597
|
+
# AI Agent Guide — {System Name}
|
|
598
|
+
|
|
599
|
+
## Project Overview
|
|
600
|
+
- **System:** {name} — {brief description from system.yaml}
|
|
601
|
+
- **Modules:** {list of modules with 1-line descriptions}
|
|
602
|
+
- **Orchestration:** Temporal ({namespace}, {target address})
|
|
603
|
+
- **Database:** {database type}
|
|
604
|
+
- **Java:** {javaVersion} / **Spring Boot:** {springBootVersion}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
7. **Always keep** (universal): DDD principles, hexagonal architecture, mapper rules, DTO rules, data flow diagrams (Command write / Query read), testing patterns
|
|
608
|
+
8. Write **everything in English**
|
|
609
|
+
9. **Limit: ≤ 1000 lines** — prune aggressively, compress examples, avoid redundancy
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
### Step 9b — Create system/VALIDATION_FLOWS.md
|
|
614
|
+
|
|
615
|
+
Generate `system/VALIDATION_FLOWS.md` with technical validation flows for the system. All information is derived from `system.yaml` and the `{module}.yaml` files.
|
|
616
|
+
|
|
617
|
+
**Mandatory structure:**
|
|
618
|
+
|
|
619
|
+
```markdown
|
|
620
|
+
# Validation Flows — {System Name}
|
|
621
|
+
|
|
622
|
+
## Prerequisites
|
|
623
|
+
- Services: {required infrastructure — DB, Temporal Server, external services}
|
|
624
|
+
- Temporal: namespace={namespace}, target={address}
|
|
625
|
+
- Startup order: {if relevant}
|
|
626
|
+
- Base URLs: {per module if different}
|
|
627
|
+
|
|
628
|
+
## 1. Module Validation
|
|
629
|
+
|
|
630
|
+
### 1.1 {Module Name}
|
|
631
|
+
|
|
632
|
+
#### CRUD Operations
|
|
633
|
+
| # | Operation | Endpoint | Payload/Params | Expected Result | Validates |
|
|
634
|
+
|---|-----------|----------|----------------|-----------------|-----------||
|
|
635
|
+
| 1 | Create | POST /x | {key fields} | 201 + entity | {invariant} |
|
|
636
|
+
| 2 | Get by ID | GET /x/{id} | — | 200 + entity | — |
|
|
637
|
+
| 3 | List | GET /x | — | 200 + page | — |
|
|
638
|
+
| 4 | Update | PUT /x/{id} | {fields} | 200 + updated | — |
|
|
639
|
+
| 5 | Delete | DELETE /x/{id} | — | 204 | — |
|
|
640
|
+
|
|
641
|
+
#### State Transitions (if module has enum transitions)
|
|
642
|
+
| # | Transition | Endpoint | Precondition | Expected | Event / Workflow Triggered |
|
|
643
|
+
|---|-----------|----------|--------------|----------|----------------------------|
|
|
644
|
+
| 1 | DRAFT→PUBLISHED | PUT /x/{id}/publish | exists in DRAFT | 200, status=PUBLISHED | XPublishedEvent → PlaceXWorkflow |
|
|
645
|
+
|
|
646
|
+
#### Business Rules
|
|
647
|
+
| # | Rule | How to Trigger | Expected Error |
|
|
648
|
+
|---|------|----------------|----------------|
|
|
649
|
+
|
|
650
|
+
(repeat per module)
|
|
651
|
+
|
|
652
|
+
## 2. Workflow Validation
|
|
653
|
+
|
|
654
|
+
### 2.1 {WorkflowName}
|
|
655
|
+
**Trigger:** {event} from {module}
|
|
656
|
+
**Saga:** {yes/no}
|
|
657
|
+
**Task Queue:** {QUEUE_NAME}
|
|
658
|
+
**Steps:**
|
|
659
|
+
1. Activity: {ActivityName} → {target module} — expected: {result}
|
|
660
|
+
2. Activity: {ActivityName} → {target module} — expected: {result}
|
|
661
|
+
**Compensation (if saga fails at step N):**
|
|
662
|
+
- Step N-1: {CompensationActivity} reverses {what}
|
|
663
|
+
**Verify:**
|
|
664
|
+
- {expected final state in each affected module}
|
|
665
|
+
|
|
666
|
+
(repeat per cross-module workflow)
|
|
667
|
+
|
|
668
|
+
## 3. On-Demand Read Validation (if read activities exist)
|
|
669
|
+
### 3.1 {ReadActivityName}: {caller workflow} → {target module}
|
|
670
|
+
| Input | Expected Output | Error Case |
|
|
671
|
+
|---|---|---|
|
|
672
|
+
| valid ID | entity data returned | 404 → workflow handles gracefully |
|
|
673
|
+
|
|
674
|
+
## 4. External Service Calls (if ports exist)
|
|
675
|
+
### 4.1 {PortName}: {module} → {external service}
|
|
676
|
+
| Method | Expected | Fallback |
|
|
677
|
+
|---|---|---|
|
|
678
|
+
|
|
679
|
+
## 5. Error & Edge Cases
|
|
680
|
+
| # | Scenario | Steps | Expected Error |
|
|
681
|
+
|---|----------|-------|----------------|
|
|
682
|
+
| 1 | Create with missing required field | POST /x without {field} | 400 + validation message |
|
|
683
|
+
| 2 | Invalid state transition | PUT /x/{id}/action when invalid state | 400/409 + business error |
|
|
684
|
+
| 3 | Saga compensation on failure | Trigger workflow, fail at step N | Previous steps compensated |
|
|
685
|
+
| 4 | Activity timeout | Simulate slow activity | Temporal retries per retryPolicy |
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
**Rules:**
|
|
689
|
+
- Each flow must be concrete: real paths, real event names, real workflow/activity names from the project
|
|
690
|
+
- Include suggested JSON payloads where useful
|
|
691
|
+
- Omit entire sections if not applicable (e.g., no ports → omit section 4)
|
|
692
|
+
- Everything in English
|
|
693
|
+
|
|
694
|
+
---
|
|
695
|
+
|
|
696
|
+
### Step 9c — Create system/USER_FLOWS.md
|
|
697
|
+
|
|
698
|
+
Generate `system/USER_FLOWS.md` with end-to-end flows from the user’s perspective.
|
|
699
|
+
|
|
700
|
+
**Mandatory structure:**
|
|
701
|
+
|
|
702
|
+
```markdown
|
|
703
|
+
# User Flows — {System Name}
|
|
704
|
+
|
|
705
|
+
## Actors
|
|
706
|
+
| Actor | Description | Modules Interacted |
|
|
707
|
+
|-------|-------------|--------------------|
|
|
708
|
+
| {Actor 1} | {role description} | {module list} |
|
|
709
|
+
|
|
710
|
+
## Flow 1: {Business Process Name}
|
|
711
|
+
**Actor:** {who}
|
|
712
|
+
**Goal:** {what they want to achieve}
|
|
713
|
+
**Preconditions:** {initial state}
|
|
714
|
+
|
|
715
|
+
### Happy Path
|
|
716
|
+
| Step | User Action | System Response | Behind the Scenes |
|
|
717
|
+
|------|-------------|-----------------|-------------------|
|
|
718
|
+
| 1 | {does X} | {sees Y} | {endpoint called, workflow started, activities invoked} |
|
|
719
|
+
| 2 | {does Z} | {sees W} | {activity completes, state changes} |
|
|
720
|
+
|
|
721
|
+
### Alternative Paths
|
|
722
|
+
| Condition | At Step | What Happens |
|
|
723
|
+
|-----------|---------|---------- ---|
|
|
724
|
+
| {condition} | {N} | {alternative outcome} |
|
|
725
|
+
|
|
726
|
+
### Error Paths
|
|
727
|
+
| Error | At Step | User Sees |
|
|
728
|
+
|-------|---------|----------|
|
|
729
|
+
| {error} | {N} | {error message/behavior, saga rollback visible effect} |
|
|
730
|
+
|
|
731
|
+
(repeat per major business flow)
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
**Rules:**
|
|
735
|
+
- Derive actors from who consumes the `exposes[]` endpoints (same source as C4 Context `Person()` nodes)
|
|
736
|
+
- Each flow is a **complete business scenario** crossing modules where applicable
|
|
737
|
+
- "Behind the Scenes" column references **workflows and activities** instead of events and topics
|
|
738
|
+
- Saga compensation reflected as **user-observable rollback behavior** (e.g., "Payment reversed, stock released")
|
|
739
|
+
- Include at least one flow per major use case path through the system
|
|
740
|
+
- Focus on user-observable behavior, not internal implementation
|
|
741
|
+
- Everything in English
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
## Refinement cycle
|
|
746
|
+
|
|
747
|
+
After delivering v1, if the user requests adjustments:
|
|
748
|
+
- Apply the **minimum change** necessary
|
|
749
|
+
- Revalidate the checklist from Step 4
|
|
750
|
+
- Update `system.md`, `c4-context.mmd`, `c4-container.mmd`, `{module}.yaml` and `{module}.md` affected
|
|
751
|
+
- Update `AGENTS.md`, `VALIDATION_FLOWS.md` and `USER_FLOWS.md` if affected by the change
|
|
752
|
+
- Deliver only the explained diff
|