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.
Files changed (151) hide show
  1. package/AGENTS.md +220 -5
  2. package/DOMAIN_YAML_GUIDE.md +188 -3
  3. package/FUTURE_FEATURES.md +33 -52
  4. package/QUICK_REFERENCE.md +8 -4
  5. package/bin/eva4j.js +70 -2
  6. package/config/defaults.json +1 -0
  7. package/docs/CAMUNDA_DMN_GUIDE.md +1380 -0
  8. package/docs/KAFKA_PRODUCTION_CONFIG.md +441 -0
  9. package/docs/RABBITMQ_PRODUCTION_CONFIG.md +227 -0
  10. package/docs/commands/ADD_RABBITMQ_CLIENT.md +192 -0
  11. package/docs/commands/EVALUATE_SYSTEM.md +290 -10
  12. package/docs/commands/GENERATE_RABBITMQ_EVENT.md +341 -0
  13. package/docs/commands/GENERATE_RABBITMQ_LISTENER.md +595 -0
  14. package/docs/commands/GENERATE_TEMPORAL_FLOW.md +52 -12
  15. package/docs/commands/INDEX.md +27 -3
  16. package/docs/prototype/TEMPORAL_COMMUNICATION_PATTERNS.md +731 -0
  17. package/docs/prototype/TEMPORAL_DESIGN_METHODOLOGY.md +740 -0
  18. package/docs/prototype/system/RISKS.md +277 -0
  19. package/docs/prototype/system/customers.yaml +133 -0
  20. package/docs/prototype/system/inventory.yaml +109 -0
  21. package/docs/prototype/system/notifications.yaml +131 -0
  22. package/docs/prototype/system/orders.yaml +241 -0
  23. package/docs/prototype/system/payments.yaml +256 -0
  24. package/docs/prototype/system/products.yaml +168 -0
  25. package/docs/prototype/system/system.yaml +269 -0
  26. package/examples/domain-endpoints-multi-aggregate.yaml +140 -0
  27. package/examples/domain-events.yaml +26 -0
  28. package/examples/domain-read-models.yaml +113 -0
  29. package/examples/system/customer.yaml +89 -0
  30. package/examples/system/orders.yaml +119 -0
  31. package/examples/system/product.yaml +27 -0
  32. package/examples/system/system.yaml +80 -0
  33. package/package.json +1 -1
  34. package/read-model-spec.md +664 -0
  35. package/src/agents/design-gap-analyst-temporal.agent.md +452 -0
  36. package/src/agents/design-gap-analyst.agent.md +383 -0
  37. package/src/agents/design-reviewer-temporal.agent.md +412 -0
  38. package/src/agents/design-reviewer.agent.md +34 -5
  39. package/src/agents/implement-use-cases.prompt.md +179 -0
  40. package/src/agents/ux-gap-analyst.agent.md +412 -0
  41. package/src/commands/add-rabbitmq-client.js +261 -0
  42. package/src/commands/add-temporal-client.js +22 -2
  43. package/src/commands/build.js +267 -11
  44. package/src/commands/evaluate-system.js +700 -13
  45. package/src/commands/generate-entities.js +560 -24
  46. package/src/commands/generate-http-exchange.js +3 -0
  47. package/src/commands/generate-kafka-event.js +3 -0
  48. package/src/commands/generate-kafka-listener.js +3 -0
  49. package/src/commands/generate-rabbitmq-event.js +665 -0
  50. package/src/commands/generate-rabbitmq-listener.js +205 -0
  51. package/src/commands/generate-record.js +2 -2
  52. package/src/commands/generate-resource.js +4 -1
  53. package/src/commands/generate-temporal-activity.js +970 -33
  54. package/src/commands/generate-temporal-flow.js +98 -38
  55. package/src/commands/generate-temporal-system.js +708 -0
  56. package/src/commands/generate-usecase.js +4 -1
  57. package/src/skills/build-system-yaml/SKILL.md +343 -2
  58. package/src/skills/build-system-yaml/references/domain-yaml-spec.md +253 -26
  59. package/src/skills/build-system-yaml/references/module-spec.md +90 -9
  60. package/src/skills/build-system-yaml/references/system-yaml-spec.md +36 -0
  61. package/src/skills/build-temporal-system/SKILL.md +752 -0
  62. package/src/skills/build-temporal-system/references/temporal-communication-patterns.md +167 -0
  63. package/src/skills/build-temporal-system/references/temporal-domain-yaml-spec.md +449 -0
  64. package/src/skills/build-temporal-system/references/temporal-module-spec.md +353 -0
  65. package/src/skills/build-temporal-system/references/temporal-system-yaml-spec.md +326 -0
  66. package/src/skills/implement-use-case/SKILL.md +350 -0
  67. package/src/skills/implement-use-case/references/use-case-patterns.md +980 -0
  68. package/src/skills/requirements-elicitation/SKILL.md +228 -0
  69. package/src/skills/requirements-elicitation/references/interview-framework.md +260 -0
  70. package/src/skills/requirements-elicitation/references/output-templates.md +368 -0
  71. package/src/utils/bounded-context-diagram.js +844 -0
  72. package/src/utils/config-manager.js +4 -2
  73. package/src/utils/domain-validator.js +495 -17
  74. package/src/utils/naming.js +20 -0
  75. package/src/utils/system-validator.js +169 -11
  76. package/src/utils/system-yaml-parser.js +318 -0
  77. package/src/utils/temporal-validator.js +497 -0
  78. package/src/utils/validator.js +3 -1
  79. package/src/utils/yaml-to-entity.js +281 -9
  80. package/templates/aggregate/AggregateRepository.java.ejs +4 -0
  81. package/templates/aggregate/AggregateRepositoryImpl.java.ejs +8 -0
  82. package/templates/aggregate/AggregateRoot.java.ejs +38 -4
  83. package/templates/aggregate/DomainEventHandler.java.ejs +116 -22
  84. package/templates/aggregate/JpaAggregateRoot.java.ejs +4 -4
  85. package/templates/aggregate/JpaEntity.java.ejs +2 -2
  86. package/templates/base/docker/rabbitmq-services.yaml.ejs +12 -0
  87. package/templates/base/resources/parameters/develop/kafka.yaml.ejs +5 -0
  88. package/templates/base/resources/parameters/develop/rabbitmq.yaml.ejs +15 -0
  89. package/templates/base/resources/parameters/develop/temporal.yaml.ejs +0 -3
  90. package/templates/base/resources/parameters/local/kafka.yaml.ejs +5 -0
  91. package/templates/base/resources/parameters/local/rabbitmq.yaml.ejs +15 -0
  92. package/templates/base/resources/parameters/local/temporal.yaml.ejs +0 -3
  93. package/templates/base/resources/parameters/production/kafka.yaml.ejs +39 -8
  94. package/templates/base/resources/parameters/production/rabbitmq.yaml.ejs +32 -0
  95. package/templates/base/resources/parameters/production/temporal.yaml.ejs +0 -3
  96. package/templates/base/resources/parameters/test/kafka.yaml.ejs +12 -6
  97. package/templates/base/resources/parameters/test/rabbitmq.yaml.ejs +15 -0
  98. package/templates/base/resources/parameters/test/temporal.yaml.ejs +0 -3
  99. package/templates/base/root/AGENTS.md.ejs +1 -1
  100. package/templates/crud/DeleteCommandHandler.java.ejs +19 -1
  101. package/templates/crud/EndpointsController.java.ejs +1 -1
  102. package/templates/crud/ScaffoldCommand.java.ejs +5 -2
  103. package/templates/crud/ScaffoldCommandHandler.java.ejs +3 -1
  104. package/templates/crud/ScaffoldQuery.java.ejs +5 -2
  105. package/templates/crud/ScaffoldQueryHandler.java.ejs +3 -1
  106. package/templates/crud/SubEntityRemoveCommand.java.ejs +1 -1
  107. package/templates/crud/UpdateCommandHandler.java.ejs +53 -2
  108. package/templates/evaluate/report.html.ejs +1447 -90
  109. package/templates/kafka-event/KafkaConfigBean.java.ejs +1 -1
  110. package/templates/kafka-event/KafkaMessageBroker.java.ejs +3 -3
  111. package/templates/ports/PortAclMapper.java.ejs +35 -0
  112. package/templates/ports/PortFeignAdapter.java.ejs +7 -22
  113. package/templates/ports/PortFeignClient.java.ejs +4 -0
  114. package/templates/ports/PortResponseDto.java.ejs +1 -1
  115. package/templates/rabbitmq-event/RabbitConfigBean.java.ejs +33 -0
  116. package/templates/rabbitmq-event/RabbitConfigExchange.java.ejs +12 -0
  117. package/templates/rabbitmq-event/RabbitMessageBroker.java.ejs +35 -0
  118. package/templates/rabbitmq-event/RabbitMessageBrokerMethod.java.ejs +9 -0
  119. package/templates/rabbitmq-listener/RabbitConfigConsumerBean.java.ejs +33 -0
  120. package/templates/rabbitmq-listener/RabbitConfigConsumerExchange.java.ejs +12 -0
  121. package/templates/rabbitmq-listener/RabbitListenerClass.java.ejs +82 -0
  122. package/templates/rabbitmq-listener/RabbitListenerSimple.java.ejs +56 -0
  123. package/templates/read-model/ReadModelDomain.java.ejs +46 -0
  124. package/templates/read-model/ReadModelJpa.java.ejs +58 -0
  125. package/templates/read-model/ReadModelJpaRepository.java.ejs +13 -0
  126. package/templates/read-model/ReadModelKafkaListener.java.ejs +64 -0
  127. package/templates/read-model/ReadModelRabbitListener.java.ejs +71 -0
  128. package/templates/read-model/ReadModelRepository.java.ejs +42 -0
  129. package/templates/read-model/ReadModelRepositoryImpl.java.ejs +85 -0
  130. package/templates/read-model/ReadModelSyncHandler.java.ejs +54 -0
  131. package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +18 -4
  132. package/templates/shared/configurations/rabbitmqConfig/RabbitMQConfig.java.ejs +100 -0
  133. package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +2 -64
  134. package/templates/shared/configurations/temporalConfig/TemporalWorkerFactoryLifecycle.java.ejs +41 -0
  135. package/templates/temporal-activity/ActivityImpl.java.ejs +68 -2
  136. package/templates/temporal-activity/ActivityInput.java.ejs +14 -0
  137. package/templates/temporal-activity/ActivityInterface.java.ejs +7 -1
  138. package/templates/temporal-activity/ActivityOutput.java.ejs +14 -0
  139. package/templates/temporal-activity/NestedType.java.ejs +12 -0
  140. package/templates/temporal-activity/SharedActivityInput.java.ejs +14 -0
  141. package/templates/temporal-activity/SharedActivityInterface.java.ejs +15 -0
  142. package/templates/temporal-activity/SharedActivityOutput.java.ejs +14 -0
  143. package/templates/temporal-activity/SharedNestedType.java.ejs +12 -0
  144. package/templates/temporal-flow/ModuleHeavyActivity.java.ejs +6 -0
  145. package/templates/temporal-flow/ModuleLightActivity.java.ejs +6 -0
  146. package/templates/temporal-flow/ModuleTemporalWorkerConfig.java.ejs +58 -0
  147. package/templates/temporal-flow/WorkFlowImpl.java.ejs +172 -12
  148. package/templates/temporal-flow/WorkFlowInput.java.ejs +11 -0
  149. package/templates/temporal-flow/WorkFlowInterface.java.ejs +5 -4
  150. package/templates/temporal-flow/WorkFlowService.java.ejs +42 -12
  151. package/COMMAND_EVALUATION.md +0 -911
@@ -0,0 +1,167 @@
1
+ # Temporal Communication Patterns — Quick Reference
2
+
3
+ Reference for understanding how modules communicate in a Temporal-based system.
4
+
5
+ ---
6
+
7
+ ## Pattern Summary
8
+
9
+ | Pattern | Coupling | Direction | Waits for response | Use case |
10
+ |---------|---------|-----------|-------------------|----------|
11
+ | **Remote Activity** | Medium | Request → Response | ✅ Yes (blocking) | Atomic operations cross-service |
12
+ | **Remote Activity + Async** | Medium | Request → Response (parallel) | ✅ Yes (non-blocking) | Multiple independent operations in parallel |
13
+ | **Child Workflow** | High | Parent → Child | ✅ Yes | Subprocesses with own lifecycle |
14
+ | **Signal External** | Low | Fire & Forget | ❌ No | Notifications between active workflows |
15
+ | **Signal + Await** | Low | Bidirectional | ✅ Yes (with wait) | Wait for external event with timeout |
16
+
17
+ ---
18
+
19
+ ## 1. Remote Activity (most common)
20
+
21
+ A workflow invokes an activity whose implementation lives in another service. Temporal routes the task via Task Queue.
22
+
23
+ **When to use:**
24
+ - Atomic operations without own lifecycle
25
+ - Workflow needs the result to continue
26
+ - Read data from another service
27
+ - Simple write in another service
28
+
29
+ **Each microservice needs:**
30
+ ```
31
+ invoker (e.g., orders):
32
+ ├── ActivityInterface.java ← interface (contract only)
33
+ └── DTOs (input/output)
34
+
35
+ executor (e.g., inventory):
36
+ ├── ActivityInterface.java ← same interface
37
+ ├── ActivityImpl.java ← implementation with DB access
38
+ └── Worker.java ← registers activity in queue
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 2. Parallel Execution with Async.function()
44
+
45
+ Execute multiple independent activities simultaneously.
46
+
47
+ **When to use:**
48
+ - 2+ activities are independent of each other
49
+ - Want to reduce total latency (max of individual times vs sum)
50
+
51
+ ```
52
+ Sequential: A(2s) → B(1s) → C(1.5s) = 4.5s
53
+ Parallel: A(2s) | B(1s) | C(1.5s) = 2s (max)
54
+ ```
55
+
56
+ **In system.yaml:** mark steps with `parallel: true`
57
+
58
+ ---
59
+
60
+ ## 3. Child Workflow
61
+
62
+ A workflow launches a sub-workflow with its own lifecycle, history, and signals.
63
+
64
+ **When to use:**
65
+ - Subprocess has **own lifecycle** (can be cancelled, queried, retried)
66
+ - Complex logic with multiple internal steps
67
+ - Need to **limit cancellation scope**
68
+ - Parent history would be too long if inlined
69
+
70
+ **Difference from Remote Activity:**
71
+ ```
72
+ Remote Activity:
73
+ - One operation → result
74
+ - No internal state
75
+ - No signals or queries
76
+
77
+ Child Workflow:
78
+ - Multiple steps with internal state
79
+ - Accepts signals
80
+ - Can be queried
81
+ - Own history in Temporal Web
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 4. Signal External (Fire & Forget)
87
+
88
+ Send a signal to an already-running workflow. No response expected.
89
+
90
+ **When to use:**
91
+ - Notify an active workflow that something happened
92
+ - No response needed
93
+ - Services are completely independent
94
+
95
+ **The sender does NOT need the receiver's interface** if using untyped stub — lowest coupling pattern.
96
+
97
+ ---
98
+
99
+ ## 5. Signal + Await (Wait with Timeout)
100
+
101
+ Combine Signal with `Workflow.await()` for request-wait pattern between services.
102
+
103
+ **When to use:**
104
+ - Need response from another service but don't want to block with activity
105
+ - Other service may take minutes, hours, or days
106
+ - Need a timeout if response doesn't arrive
107
+ - Human approvals, external verifications, batch processes
108
+
109
+ ---
110
+
111
+ ## Mapping to system.yaml
112
+
113
+ | Pattern | Representation in system.yaml |
114
+ |---------|------------------------------|
115
+ | Remote Activity | `steps: [{ activity: X, target: Y, type: sync }]` |
116
+ | Parallel Activity | `steps: [{ activity: X, parallel: true }]` |
117
+ | Async Activity | `steps: [{ activity: X, type: async }]` |
118
+ | Child Workflow | Not in system.yaml directly — declare in domain.yaml `workflows:` |
119
+ | Signal | Implicit in `wait:` steps in domain.yaml workflows |
120
+
121
+ ---
122
+
123
+ ## Activity Data Flow Rules
124
+
125
+ ### Rule 0: Activities are invoked ONLY from workflows
126
+
127
+ Activities are Temporal constructs — they can ONLY be invoked from within a workflow execution context. Handlers, use cases, and REST controllers do NOT have access to activity stubs. If a REST endpoint needs data from another module, the handler must emit a Domain Event that triggers a workflow, and the workflow orchestrates the activity calls.
128
+
129
+ ```
130
+ ✅ Workflow invokes activity:
131
+ PlaceOrderWorkflow → GetProductsByIds (via Temporal stub)
132
+
133
+ ❌ Handler invokes activity:
134
+ AddToCartCommandHandler → GetProductById (WRONG — not a workflow context)
135
+
136
+ ✅ Correct handler pattern:
137
+ AddToCartCommandHandler → save(cart) → DomainEvent → AddToCartWorkflow → GetProductById
138
+ ```
139
+
140
+ This applies to ALL activities — both cross-module and local. Even a module's own activities are invoked from its single-module `workflows:` in domain.yaml, never from handlers.
141
+
142
+ ### Rule 1: Activities access ONLY their own module's DB
143
+
144
+ ```
145
+ ✅ inventory.ReserveStock → reads inventory.stock table
146
+ ❌ inventory.ReserveStock → reads customers.customer table
147
+ ```
148
+
149
+ ### Rule 2: Workflows assemble cross-module data
150
+
151
+ ```
152
+ Workflow:
153
+ Step 1: GetCustomerById → customers (get email, name)
154
+ Step 2: GetProductsByIds → products (get prices)
155
+ Step 3: ReserveStock → inventory (pass assembled data)
156
+ Step 4: NotifyOrderPlaced → notifications (pass customer email, name, total)
157
+ ```
158
+
159
+ ### Rule 3: Notification activities receive ALL data as input
160
+
161
+ ```
162
+ ✅ NotifyOrderPlaced(orderId, email, firstName, totalAmount)
163
+ → Has everything it needs, zero lookups
164
+
165
+ ❌ NotifyOrderPlaced(orderId, customerId)
166
+ → Would need to look up customer data — HIDDEN COUPLING
167
+ ```
@@ -0,0 +1,449 @@
1
+ # Temporal domain.yaml per module — Complete Specification
2
+
3
+ Reference for building `system/{module}.yaml` in a Temporal-based system. This file is the input for `eva g entities <module>`.
4
+
5
+ > **PATH RULE:** Each module file MUST be saved inside the `system/` directory as `system/{module-name}.yaml` (e.g., `system/orders.yaml`, `system/payments.yaml`). NEVER save module YAML files at the project root.
6
+
7
+ ---
8
+
9
+ ## Expert role per module
10
+
11
+ When building each `system/{module}.yaml`, activate the domain expert role:
12
+
13
+ - **`orders`** → lifecycles, states, invariants, item relationships, calculated totals
14
+ - **`payments`** → payment methods, retries, terminal states, double-charge prevention
15
+ - **`inventory`** → available vs. reserved stock, movements, replenishment
16
+ - **`notifications`** → channels, templates, idempotency, retries
17
+
18
+ Propose necessary fields not mentioned, expressive Value Objects, implicit invariants, realistic state transitions. If you need specific business rules, ask.
19
+
20
+ ---
21
+
22
+ ## Absolute restrictions
23
+
24
+ 1. ❌ **No `@ManyToOne`/`@OneToMany` between aggregates** — cross-aggregate references are IDs with `reference:`
25
+ 2. ❌ **No audit fields in `fields:`** — `audit.enabled: true` generates them
26
+ 3. ❌ **No `defaultValue` on non `readOnly` fields**
27
+ 4. ❌ **No `transitions` without `initialValue`** in the enum
28
+ 5. ❌ **No invented modules in `reference.module`** — only those in `system/system.yaml`
29
+ 6. ❌ **No duplicate `endpoints:` from `system.yaml → exposes:`**
30
+ 7. ❌ **`endpoints:` NEVER a flat list** — always `{ basePath, versions: [{ version, operations }] }`. If the module has **2+ aggregates**, use `basePath: ""` and absolute paths per operation
31
+ 8. ❌ **No `listeners:` section** — Temporal replaces Kafka consumers
32
+ 9. ❌ **No `readModels:` section** — on-demand reads via Activities replace local projections
33
+ 10. ❌ **No `ports:` for internal modules** — only for external services (non-Temporal)
34
+ 11. ❌ **All in English**
35
+ 12. ❌ **No `lifecycle:` and `triggers:` on the same event** — mutually exclusive
36
+ 13. ❌ **No `lifecycle: softDelete` without `hasSoftDelete: true`** on root entity
37
+ 14. ❌ **No `lifecycle: delete` with `hasSoftDelete: true`**
38
+ 15. ❌ **No fields in lifecycle events not in the root entity** — `C2-010`
39
+ 16. ❌ **No `topic:` on events** — Temporal replaces Kafka topics
40
+ 17. ❌ **Events with `notifies:` must reference workflows in system.yaml** — never arbitrary names
41
+ 18. ❌ **Activities must not do cross-module data lookups** — all data comes via input
42
+ 19. ❌ **Activities invoked ONLY from workflows** — never from handlers, use cases, or REST controllers. If a handler needs cross-module data, emit an event with `notifies:` to trigger a workflow that invokes the read activity. Even local activities are invoked from single-module `workflows:` in domain.yaml, not from handlers
43
+
44
+ ---
45
+
46
+ ## Inference from system.yaml
47
+
48
+ | Source in system.yaml | Destination in domain.yaml |
49
+ |---|---|
50
+ | `modules[x].exposes[]` | `endpoints:` with `basePath` + `versions[].operations[]` |
51
+ | `workflows[].steps[]` where `target = module` | `activities:` — the module's capabilities |
52
+ | `workflows[].trigger` where `module = module` | `events:` with `notifies:` |
53
+ | None — internal processes | `workflows:` — single-module internal flows |
54
+
55
+ ---
56
+
57
+ ## Complete module.yaml structure
58
+
59
+ ```yaml
60
+ aggregates:
61
+ - name: Order # PascalCase
62
+ entities:
63
+ - name: order # camelCase — root entity
64
+ isRoot: true
65
+ tableName: orders # snake_case
66
+ hasSoftDelete: false
67
+ audit:
68
+ enabled: true
69
+ trackUser: false
70
+ fields:
71
+ - name: id
72
+ type: String
73
+ - name: customerId
74
+ type: String
75
+ reference:
76
+ aggregate: Customer
77
+ module: customers
78
+ - name: totalAmount
79
+ type: BigDecimal
80
+ readOnly: true
81
+ defaultValue: "0.00"
82
+ - name: status
83
+ type: OrderStatus
84
+ readOnly: true
85
+ relationships:
86
+ - type: OneToMany
87
+ target: OrderItem
88
+ mappedBy: order
89
+ cascade: [PERSIST, MERGE, REMOVE]
90
+ fetch: LAZY
91
+
92
+ - name: orderItem
93
+ tableName: order_items
94
+ fields:
95
+ - name: id
96
+ type: String
97
+ - name: productId
98
+ type: String
99
+ reference:
100
+ aggregate: Product
101
+ module: products
102
+
103
+ valueObjects:
104
+ - name: ShippingAddress
105
+ fields:
106
+ - name: street
107
+ type: String
108
+ - name: city
109
+ type: String
110
+
111
+ enums:
112
+ - name: OrderStatus
113
+ initialValue: PENDING
114
+ transitions:
115
+ - from: PENDING
116
+ to: CONFIRMED
117
+ method: confirm
118
+ - from: PENDING
119
+ to: CANCELLED
120
+ method: cancel
121
+ values: [PENDING, CONFIRMED, CANCELLED]
122
+
123
+ events:
124
+ # Events WITH notifies: → trigger cross-module workflows
125
+ - name: OrderPlacedEvent
126
+ lifecycle: create
127
+ fields:
128
+ - name: orderId
129
+ type: String
130
+ - name: placedAt
131
+ type: LocalDateTime
132
+ notifies:
133
+ - workflow: PlaceOrderWorkflow
134
+
135
+ # Events with triggers: → state transitions
136
+ - name: OrderCancelledEvent
137
+ triggers:
138
+ - cancel
139
+ fields:
140
+ - name: orderId
141
+ type: String
142
+ notifies:
143
+ - workflow: CancelOrderWorkflow
144
+
145
+ # Events WITHOUT notifies: → internal Domain Events
146
+ - name: CustomerUpdatedEvent
147
+ lifecycle: update
148
+ fields:
149
+ - name: customerId
150
+ type: String
151
+ # NO notifies → Domain Event internal
152
+
153
+ # ─── Activities this module EXPOSES ──────────────────────────────────────────
154
+ activities:
155
+ - name: GetOrderDetails
156
+ type: light
157
+ description: "Gets full order details including items"
158
+ input:
159
+ - name: orderId
160
+ type: String
161
+ output:
162
+ - name: customerId
163
+ type: String
164
+ - name: items
165
+ type: List<OrderItemDetail>
166
+ - name: totalAmount
167
+ type: BigDecimal
168
+ nestedTypes:
169
+ - name: OrderItemDetail
170
+ fields:
171
+ - name: productId
172
+ type: String
173
+ - name: quantity
174
+ type: Integer
175
+ - name: unitPrice
176
+ type: BigDecimal
177
+ timeout: 5s
178
+
179
+ - name: ConfirmOrder
180
+ type: light
181
+ description: "Confirms the order after successful payment"
182
+ input:
183
+ - name: orderId
184
+ type: String
185
+ timeout: 5s
186
+
187
+ - name: CancelExpiredOrder
188
+ type: light
189
+ description: "Cancels order that did not receive payment in time"
190
+ input:
191
+ - name: orderId
192
+ type: String
193
+ timeout: 5s
194
+
195
+ # ─── Single-module workflows (internal) ─────────────────────────────────────
196
+ workflows:
197
+ - name: ExpireOrderWorkflow
198
+ description: "Cancels order if payment not received within timeout"
199
+ trigger:
200
+ on: orderCreated
201
+ taskQueue: ORDER_WORKFLOW_QUEUE
202
+ steps:
203
+ - wait: paymentCompleted
204
+ timeout: 30m
205
+ - activity: CancelExpiredOrder
206
+ timeout: 5s
207
+
208
+ endpoints:
209
+ basePath: /orders
210
+ versions:
211
+ - version: v1
212
+ operations:
213
+ - useCase: CreateOrder
214
+ method: POST
215
+ path: /
216
+ - useCase: GetOrder
217
+ method: GET
218
+ path: /{id}
219
+ - useCase: FindAllOrders
220
+ method: GET
221
+ path: /
222
+
223
+ # ─── Multi-aggregate module example ─────────────────────────────────────────────
224
+ # When a module contains 2+ aggregates (e.g. Product + Category),
225
+ # use basePath: "" (empty string, NOT "/") and absolute paths:
226
+ #
227
+ # endpoints:
228
+ # basePath: ""
229
+ # versions:
230
+ # - version: v1
231
+ # operations:
232
+ # - useCase: CreateProduct
233
+ # method: POST
234
+ # path: /products
235
+ # - useCase: GetProduct
236
+ # method: GET
237
+ # path: /products/{id}
238
+ # - useCase: CreateCategory
239
+ # method: POST
240
+ # path: /categories
241
+ # - useCase: FindProductsByCategory
242
+ # method: GET
243
+ # path: /categories/{id}/products
244
+
245
+ # ─── ports: ONLY for EXTERNAL services ──────────────────────────────────────
246
+ # ports:
247
+ # - name: processCharge
248
+ # service: PaymentGatewayService
249
+ # target: payment-gateway
250
+ # baseUrl: https://api.payments.example.com
251
+ # http: POST /charges
252
+ ```
253
+
254
+ ---
255
+
256
+ ## Activities section — detailed specification
257
+
258
+ ### Activities vs Ports — invocation rules
259
+
260
+ | Aspect | `ports:` | `activities:` |
261
+ |---|---|---|
262
+ | What it represents | External service clients (Feign) | Module capabilities (Temporal) |
263
+ | Target | Payment gateways, email providers, third-party APIs | Other modules or same module |
264
+ | Who can invoke | Handlers, use cases, activity implementations | **ONLY workflows** (via Temporal stubs) |
265
+ | Where declared | `ports:` in domain.yaml | `activities:` in domain.yaml |
266
+ | Invocation mechanism | Feign HTTP call | Temporal task queue dispatch |
267
+
268
+ > **Key takeaway:** A handler that needs data from another module must trigger a workflow (via Domain Event + `notifies:`). The workflow then invokes the appropriate read activity. Handlers NEVER inject or call activity interfaces.
269
+
270
+ ### Activity properties
271
+
272
+ | Property | Required | Description |
273
+ |---|---|---|
274
+ | `name` | ✅ | PascalCase activity name |
275
+ | `type` | ✅ | `light` (< 30s) or `heavy` (up to 2min) |
276
+ | `description` | ❌ | What the activity does |
277
+ | `input` | ✅ | List of input fields with `name` and `type` |
278
+ | `output` | ❌ | List of output fields (omit for void activities) |
279
+ | `timeout` | ❌ | Activity timeout (e.g., `5s`, `30s`) |
280
+ | `compensation` | ❌ | Name of the compensating activity |
281
+ | `retryPolicy` | ❌ | Retry configuration |
282
+ | `nestedTypes` | ❌ | Complex types used in input/output |
283
+ | `externalTypes` | ❌ | Types defined in another module |
284
+
285
+ ### Retry policy
286
+
287
+ ```yaml
288
+ retryPolicy:
289
+ maxAttempts: 3
290
+ initialInterval: 1s
291
+ backoffCoefficient: 2.0
292
+ ```
293
+
294
+ ### External types
295
+
296
+ When an activity receives types defined in another module:
297
+
298
+ ```yaml
299
+ activities:
300
+ - name: ReserveStock
301
+ input:
302
+ - name: items
303
+ type: List<OrderItemDetail>
304
+ externalTypes:
305
+ - name: OrderItemDetail
306
+ module: orders
307
+ ```
308
+
309
+ ### Activity type classification
310
+
311
+ | Module role | Activity types | Examples |
312
+ |---|---|---|
313
+ | Data provider | Read activities | `GetCustomerById`, `GetProductsByIds` |
314
+ | Executor | Write + compensation | `ReserveStock` / `ReleaseStock` |
315
+ | Reactor | Notification activities | `NotifyOrderPlaced` |
316
+ | Orchestrator (local) | State transition activities | `ConfirmOrder`, `CancelExpiredOrder` |
317
+
318
+ ---
319
+
320
+ ## Events with `notifies:` — detailed specification
321
+
322
+ ### When to use `notifies:`
323
+
324
+ - The event triggers a **cross-module workflow** defined in `system.yaml`
325
+ - There is a **real business effect** in another module (not data sync)
326
+
327
+ ### When NOT to use `notifies:`
328
+
329
+ - The event is purely internal (no cross-module reaction)
330
+ - The data is only needed for sync (use on-demand reads instead)
331
+
332
+ ```yaml
333
+ events:
334
+ # ✅ Real business effect → notifies
335
+ - name: OrderPlacedEvent
336
+ lifecycle: create
337
+ notifies:
338
+ - workflow: PlaceOrderWorkflow
339
+
340
+ # ✅ Internal event → NO notifies
341
+ - name: CustomerUpdatedEvent
342
+ lifecycle: update
343
+ # On-demand reads: GetCustomerById activity
344
+ ```
345
+
346
+ ---
347
+
348
+ ## Workflows section (single-module) — detailed specification
349
+
350
+ ### When a workflow is single-module
351
+
352
+ - All its activities belong to the **same module**
353
+ - It doesn't need data from other bounded contexts
354
+ - It's an internal process (retry, timeout, scheduling, verification)
355
+ - Other modules don't need to know it exists
356
+
357
+ ### Step types in single-module workflows
358
+
359
+ | Step type | Property | Description |
360
+ |---|---|---|
361
+ | Activity | `activity:` | Invoke a local activity |
362
+ | Wait | `wait:` + `timeout:` | Wait for a signal with timeout |
363
+
364
+ ```yaml
365
+ workflows:
366
+ - name: RetryChargeWorkflow
367
+ trigger:
368
+ on: chargeFailed
369
+ taskQueue: PAYMENT_WORKFLOW_QUEUE
370
+ steps:
371
+ - activity: RetryCharge
372
+ retryPolicy:
373
+ maxAttempts: 3
374
+ initialInterval: 2s
375
+ backoffCoefficient: 2.0
376
+ - activity: MarkPaymentFailed
377
+ ```
378
+
379
+ ---
380
+
381
+ ## Visibility of fields
382
+
383
+ | Configuration | Business constructor | CreateDto | ResponseDto |
384
+ |---|---|---|---|
385
+ | Normal | ✅ | ✅ | ✅ |
386
+ | `readOnly: true` | ❌ | ❌ | ✅ |
387
+ | `readOnly` + `defaultValue` | ⚡ assigned with default | ❌ | ✅ |
388
+ | `hidden: true` | ✅ | ✅ | ❌ |
389
+ | Both flags | ❌ | ❌ | ❌ |
390
+
391
+ ---
392
+
393
+ ## Supported data types
394
+
395
+ | YAML | Java |
396
+ |---|---|
397
+ | `String` | String |
398
+ | `Integer` | Integer |
399
+ | `Long` | Long |
400
+ | `BigDecimal` | BigDecimal |
401
+ | `Boolean` | Boolean |
402
+ | `LocalDate` | LocalDate |
403
+ | `LocalDateTime` | LocalDateTime |
404
+ | `LocalTime` | LocalTime |
405
+ | `Instant` | Instant |
406
+ | `UUID` | UUID |
407
+
408
+ ---
409
+
410
+ ## JSR-303 Validations
411
+
412
+ Declare in `fields[].validations` — applied **only** in Command and CreateDto.
413
+
414
+ ```yaml
415
+ validations:
416
+ - type: NotBlank
417
+ message: "Required"
418
+ - type: Email
419
+ message: "Invalid email"
420
+ - type: Size
421
+ min: 3
422
+ max: 50
423
+ - type: Positive
424
+ ```
425
+
426
+ ---
427
+
428
+ ## Checklist per module
429
+
430
+ - [ ] Root entity has `id` field of type String
431
+ - [ ] `isRoot: true` on exactly one entity per aggregate
432
+ - [ ] `audit:` section present on root entity
433
+ - [ ] `readOnly:` fields with `defaultValue:` where appropriate
434
+ - [ ] `reference:` on cross-aggregate ID fields
435
+ - [ ] `endpoints:` as object with `basePath` + `versions` (NOT flat list)
436
+ - [ ] Module with 1 aggregate → `basePath: /resource` and relative paths
437
+ - [ ] Module with 2+ aggregates → `basePath: ""` and absolute paths per operation
438
+ - [ ] `events:` with `notifies:` only for cross-module workflow triggers
439
+ - [ ] `events:` without `notifies:` for internal Domain Events
440
+ - [ ] `activities:` section declares all capabilities referenced in system.yaml workflows
441
+ - [ ] Activity `input:` contains all data the activity needs (no cross-module lookups)
442
+ - [ ] `compensation:` declared for reversible activities
443
+ - [ ] No handler or use case directly invokes an activity — all activity calls go through workflows
444
+ - [ ] `workflows:` only for single-module internal flows
445
+ - [ ] No `listeners:` section (Temporal replaces it)
446
+ - [ ] No `readModels:` section (on-demand reads replace it)
447
+ - [ ] `ports:` only for external services (if any)
448
+ - [ ] No `topic:` on events
449
+ - [ ] All in English