eva4j 1.0.14 → 1.0.16
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/skills/skill-creator/LICENSE.txt +202 -0
- package/.agents/skills/skill-creator/SKILL.md +485 -0
- package/.agents/skills/skill-creator/agents/analyzer.md +274 -0
- package/.agents/skills/skill-creator/agents/comparator.md +202 -0
- package/.agents/skills/skill-creator/agents/grader.md +223 -0
- package/.agents/skills/skill-creator/assets/eval_review.html +146 -0
- package/.agents/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/.agents/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/.agents/skills/skill-creator/references/schemas.md +430 -0
- package/.agents/skills/skill-creator/scripts/__init__.py +0 -0
- package/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/.agents/skills/skill-creator/scripts/generate_report.py +326 -0
- package/.agents/skills/skill-creator/scripts/improve_description.py +247 -0
- package/.agents/skills/skill-creator/scripts/package_skill.py +136 -0
- package/.agents/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/.agents/skills/skill-creator/scripts/run_eval.py +310 -0
- package/.agents/skills/skill-creator/scripts/run_loop.py +328 -0
- package/.agents/skills/skill-creator/scripts/utils.py +47 -0
- package/AGENTS.md +268 -6
- package/COMMAND_EVALUATION.md +15 -16
- package/DOMAIN_YAML_GUIDE.md +430 -14
- package/FUTURE_FEATURES.md +1627 -1168
- package/README.md +461 -13
- package/bin/eva4j.js +32 -14
- package/config/defaults.json +1 -0
- package/docs/commands/EVALUATE_SYSTEM.md +746 -261
- package/docs/commands/EXPORT_DIAGRAM.md +153 -0
- package/docs/commands/GENERATE_ENTITIES.md +599 -6
- package/docs/commands/INDEX.md +7 -0
- package/examples/domain-events.yaml +166 -20
- package/examples/domain-listeners.yaml +212 -0
- package/examples/domain-one-to-many.yaml +1 -0
- package/examples/domain-one-to-one.yaml +1 -0
- package/examples/domain-ports.yaml +414 -0
- package/examples/domain-soft-delete.yaml +47 -44
- package/examples/system/notification.yaml +147 -0
- package/examples/system/product.yaml +185 -0
- package/examples/system/system.yaml +112 -0
- package/examples/system-report.html +971 -0
- package/examples/system.yaml +46 -3
- package/package.json +2 -1
- package/src/agents/design-reviewer.agent.md +163 -0
- package/src/commands/build.js +714 -0
- package/src/commands/create.js +1 -0
- package/src/commands/detach.js +149 -108
- package/src/commands/evaluate-system.js +234 -8
- package/src/commands/export-diagram.js +522 -0
- package/src/commands/generate-entities.js +685 -66
- package/src/commands/generate-http-exchange.js +2 -0
- package/src/commands/generate-kafka-event.js +43 -10
- package/src/generators/base-generator.js +18 -6
- package/src/generators/postman-generator.js +188 -0
- package/src/generators/shared-generator.js +12 -2
- package/src/skills/build-system-yaml/SKILL.md +323 -0
- package/src/skills/build-system-yaml/references/domain-yaml-spec.md +410 -0
- package/src/skills/build-system-yaml/references/module-spec.md +367 -0
- package/src/skills/build-system-yaml/references/system-yaml-spec.md +178 -0
- package/src/utils/config-manager.js +54 -0
- package/src/utils/context-builder.js +1 -0
- package/src/utils/domain-diagram.js +192 -0
- package/src/utils/domain-validator.js +1020 -0
- package/src/utils/fake-data.js +376 -0
- package/src/utils/system-validator.js +319 -199
- package/src/utils/yaml-to-entity.js +272 -7
- package/templates/aggregate/AggregateMapper.java.ejs +3 -2
- package/templates/aggregate/AggregateRepository.java.ejs +3 -2
- package/templates/aggregate/AggregateRepositoryImpl.java.ejs +6 -5
- package/templates/aggregate/AggregateRoot.java.ejs +60 -2
- package/templates/aggregate/DomainEventHandler.java.ejs +4 -1
- package/templates/aggregate/DomainEventRecord.java.ejs +24 -8
- package/templates/aggregate/DomainEventSnapshot.java.ejs +46 -0
- package/templates/aggregate/JpaAggregateRoot.java.ejs +6 -0
- package/templates/base/docker/Dockerfile.ejs +21 -0
- package/templates/base/gradle/build.gradle.ejs +3 -2
- package/templates/base/root/AGENTS.md.ejs +306 -45
- package/templates/crud/ApplicationMapper.java.ejs +4 -0
- package/templates/crud/Controller.java.ejs +4 -4
- package/templates/crud/CreateCommand.java.ejs +4 -0
- package/templates/crud/CreateCommandHandler.java.ejs +3 -6
- package/templates/crud/CreateItemDto.java.ejs +4 -0
- package/templates/crud/CreateValueObjectDto.java.ejs +4 -0
- package/templates/crud/DeleteCommandHandler.java.ejs +12 -6
- package/templates/crud/EndpointsController.java.ejs +6 -6
- package/templates/crud/FindByQuery.java.ejs +1 -1
- package/templates/crud/FindByQueryHandler.java.ejs +3 -9
- package/templates/crud/GetQueryHandler.java.ejs +2 -5
- package/templates/crud/ListQuery.java.ejs +1 -1
- package/templates/crud/ListQueryHandler.java.ejs +8 -14
- package/templates/crud/ScaffoldCommandHandler.java.ejs +2 -4
- package/templates/crud/ScaffoldQuery.java.ejs +3 -2
- package/templates/crud/ScaffoldQueryHandler.java.ejs +5 -6
- package/templates/crud/SubEntityAddCommand.java.ejs +4 -0
- package/templates/crud/SubEntityAddCommandHandler.java.ejs +2 -6
- package/templates/crud/SubEntityRemoveCommandHandler.java.ejs +2 -7
- package/templates/crud/TransitionCommandHandler.java.ejs +2 -6
- package/templates/crud/UpdateCommand.java.ejs +4 -0
- package/templates/crud/UpdateCommandHandler.java.ejs +2 -5
- package/templates/evaluate/report.html.ejs +394 -2
- package/templates/kafka-event/DomainEventHandlerMethod.ejs +3 -1
- package/templates/kafka-event/Event.java.ejs +9 -0
- package/templates/kafka-listener/KafkaController.java.ejs +1 -1
- package/templates/kafka-listener/KafkaListenerClass.java.ejs +1 -1
- package/templates/kafka-listener/ListenerClass.java.ejs +65 -0
- package/templates/kafka-listener/ListenerCommand.java.ejs +31 -0
- package/templates/kafka-listener/ListenerCommandHandler.java.ejs +25 -0
- package/templates/kafka-listener/ListenerIntegrationEvent.java.ejs +37 -0
- package/templates/kafka-listener/ListenerMethod.java.ejs +1 -1
- package/templates/kafka-listener/ListenerNestedType.java.ejs +28 -0
- package/templates/mock/MockEvent.java.ejs +10 -0
- package/templates/mock/MockMessageBrokerImpl.java.ejs +35 -0
- package/templates/mock/MockMessageBrokerImplMethod.java.ejs +6 -0
- package/templates/mock/SpringEventListener.java.ejs +61 -0
- package/templates/ports/PortDomainModel.java.ejs +35 -0
- package/templates/ports/PortFeignAdapter.java.ejs +67 -0
- package/templates/ports/PortFeignClient.java.ejs +45 -0
- package/templates/ports/PortFeignConfig.java.ejs +24 -0
- package/templates/ports/PortInterface.java.ejs +45 -0
- package/templates/ports/PortNestedType.java.ejs +28 -0
- package/templates/ports/PortRequestDto.java.ejs +30 -0
- package/templates/ports/PortResponseDto.java.ejs +28 -0
- package/templates/postman/Collection.json.ejs +1 -1
- package/templates/postman/UnifiedCollection.json.ejs +185 -0
- package/templates/shared/annotations/LogAfter.java.ejs +2 -0
- package/templates/shared/annotations/LogBefore.java.ejs +2 -0
- package/templates/shared/annotations/LogExceptions.java.ejs +2 -0
- package/templates/shared/annotations/LogLevel.java.ejs +8 -0
- package/templates/shared/annotations/LogTimer.java.ejs +1 -0
- package/templates/shared/annotations/Loggable.java.ejs +17 -0
- package/templates/shared/configurations/eventPublicationConfig/EventPublicationSchemaConfig.java.ejs +109 -0
- package/templates/shared/configurations/loggerConfig/HandlerLogs.java.ejs +120 -32
- package/templates/shared/errorMessage/ErrorResponse.java.ejs +23 -0
- package/templates/shared/handlerException/HandlerExceptions.java.ejs +99 -79
- package/src/commands/generate-system.js +0 -243
- package/templates/base/root/skill-build-domain-yaml-references-generate-entities.md.ejs +0 -1103
- package/templates/base/root/skill-build-domain-yaml.ejs +0 -292
- package/templates/base/root/skill-build-system-yaml.ejs +0 -252
- package/templates/shared/errorMessage/ErrorMessage.java.ejs +0 -5
- package/templates/shared/errorMessage/FullErrorMessage.java.ejs +0 -9
- package/templates/shared/errorMessage/ShortErrorMessage.java.ejs +0 -6
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
# domain.yaml por módulo — Especificación completa
|
|
2
|
+
|
|
3
|
+
Referencia técnica para construir `system/{module}.yaml` — el modelo de dominio de cada módulo. Este archivo es el input para `eva g entities <module>`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Rol de experto por módulo
|
|
8
|
+
|
|
9
|
+
Al construir cada `system/{module}.yaml`, activa el rol de experto en el dominio específico:
|
|
10
|
+
|
|
11
|
+
- **`orders`** → ciclos de vida, estados, invariantes, relaciones con items, totales calculados
|
|
12
|
+
- **`payments`** → métodos de pago, reintentos, estados terminales, doble cobro
|
|
13
|
+
- **`inventory`** → stock disponible vs. reservado, movimientos, reposición
|
|
14
|
+
- **`notifications`** → canales, plantillas, idempotencia, reintentos
|
|
15
|
+
|
|
16
|
+
Propón campos necesarios no mencionados, Value Objects expresivos, invariantes implícitas, transiciones de estado realistas. Si necesitas reglas de negocio específicas de la empresa, pregunta.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Restricciones absolutas
|
|
21
|
+
|
|
22
|
+
1. ❌ **No `@ManyToOne`/`@OneToMany` entre agregados** — referencias cross-aggregate son IDs con `reference:`
|
|
23
|
+
2. ❌ **No campos de auditoría en `fields:`** (`createdAt`, `updatedAt`, `createdBy`, `updatedBy`) — `audit.enabled: true` los genera
|
|
24
|
+
3. ❌ **No `defaultValue` en campos no `readOnly`**
|
|
25
|
+
4. ❌ **No `transitions` sin `initialValue`** en el enum
|
|
26
|
+
5. ❌ **No inventar módulos en `reference.module`** — solo los de `system/system.yaml`
|
|
27
|
+
6. ❌ **No duplicar en `endpoints:`** lo de `system.yaml → exposes:`
|
|
28
|
+
7. ❌ **`endpoints:` NUNCA es lista plana** — siempre `{ basePath, versions: [{ version, operations }] }`
|
|
29
|
+
8. ❌ **No inventar eventos** — deben coincidir con `integrations.async[]` donde `producer` es este módulo
|
|
30
|
+
9. ❌ **No inventar listeners** — deben coincidir con `integrations.async[].consumers[]` donde `module` es este módulo
|
|
31
|
+
10. ❌ **No inventar ports** — deben coincidir con `integrations.sync[]` donde `caller` es este módulo
|
|
32
|
+
11. ❌ **Todo en inglés**
|
|
33
|
+
12. ❌ **No transiciones sin evidencia de activación** — toda transición activada por `ports:` o scheduler necesita un domain event con `triggers`
|
|
34
|
+
13. ❌ **No reutilizar `service:` en `ports[]` entre módulos** — nombres propios del bounded context
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Inferencia desde system.yaml
|
|
39
|
+
|
|
40
|
+
| Fuente en system.yaml | Destino en domain.yaml |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `modules[x].exposes[]` | `endpoints:` con `basePath` + `versions[].operations[]` |
|
|
43
|
+
| `integrations.async[]` donde `producer = módulo` | `events:` |
|
|
44
|
+
| `integrations.async[].consumers[]` donde `module = módulo` | `listeners:` (con `useCase`) |
|
|
45
|
+
| `integrations.sync[]` donde `caller = módulo` | `ports:` (con `service`, `http`) |
|
|
46
|
+
|
|
47
|
+
### Formato correcto de endpoints
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
# ❌ NUNCA — lista plana (el generador la ignora)
|
|
51
|
+
endpoints:
|
|
52
|
+
- method: POST
|
|
53
|
+
path: /orders
|
|
54
|
+
useCase: CreateOrder
|
|
55
|
+
|
|
56
|
+
# ✅ SIEMPRE — objeto con basePath + versions
|
|
57
|
+
endpoints:
|
|
58
|
+
basePath: /orders
|
|
59
|
+
versions:
|
|
60
|
+
- version: v1
|
|
61
|
+
operations:
|
|
62
|
+
- useCase: CreateOrder
|
|
63
|
+
method: POST
|
|
64
|
+
path: /
|
|
65
|
+
- useCase: GetOrder
|
|
66
|
+
method: GET
|
|
67
|
+
path: /{id}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Estructura completa del module.yaml
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
aggregates:
|
|
76
|
+
- name: Order # PascalCase
|
|
77
|
+
entities:
|
|
78
|
+
- name: order # camelCase — entidad raíz
|
|
79
|
+
isRoot: true
|
|
80
|
+
tableName: orders # snake_case
|
|
81
|
+
hasSoftDelete: false
|
|
82
|
+
audit:
|
|
83
|
+
enabled: true # adds createdAt, updatedAt
|
|
84
|
+
trackUser: false # adds createdBy, updatedBy
|
|
85
|
+
fields:
|
|
86
|
+
- name: id
|
|
87
|
+
type: String
|
|
88
|
+
- name: orderNumber
|
|
89
|
+
type: String
|
|
90
|
+
validations:
|
|
91
|
+
- type: NotBlank
|
|
92
|
+
message: "Order number is required"
|
|
93
|
+
- name: totalAmount
|
|
94
|
+
type: BigDecimal
|
|
95
|
+
readOnly: true
|
|
96
|
+
defaultValue: "0.00"
|
|
97
|
+
- name: status
|
|
98
|
+
type: OrderStatus
|
|
99
|
+
readOnly: true
|
|
100
|
+
- name: processingToken
|
|
101
|
+
type: String
|
|
102
|
+
hidden: true
|
|
103
|
+
- name: customerId
|
|
104
|
+
type: String
|
|
105
|
+
reference:
|
|
106
|
+
aggregate: Customer
|
|
107
|
+
module: customers
|
|
108
|
+
relationships:
|
|
109
|
+
- type: OneToMany
|
|
110
|
+
target: OrderItem
|
|
111
|
+
mappedBy: order
|
|
112
|
+
cascade: [PERSIST, MERGE, REMOVE]
|
|
113
|
+
fetch: LAZY
|
|
114
|
+
|
|
115
|
+
- name: orderItem # entidad secundaria
|
|
116
|
+
tableName: order_items
|
|
117
|
+
fields:
|
|
118
|
+
- name: id
|
|
119
|
+
type: String
|
|
120
|
+
- name: productId
|
|
121
|
+
type: String
|
|
122
|
+
- name: quantity
|
|
123
|
+
type: Integer
|
|
124
|
+
validations:
|
|
125
|
+
- type: Min
|
|
126
|
+
value: 1
|
|
127
|
+
- name: unitPrice
|
|
128
|
+
type: BigDecimal
|
|
129
|
+
|
|
130
|
+
valueObjects:
|
|
131
|
+
- name: ShippingAddress
|
|
132
|
+
fields:
|
|
133
|
+
- name: street
|
|
134
|
+
type: String
|
|
135
|
+
- name: city
|
|
136
|
+
type: String
|
|
137
|
+
- name: zipCode
|
|
138
|
+
type: String
|
|
139
|
+
methods:
|
|
140
|
+
- name: format
|
|
141
|
+
returnType: String
|
|
142
|
+
parameters: []
|
|
143
|
+
body: "return street + \", \" + city + \" \" + zipCode;"
|
|
144
|
+
|
|
145
|
+
enums:
|
|
146
|
+
- name: OrderStatus
|
|
147
|
+
initialValue: PENDING
|
|
148
|
+
transitions:
|
|
149
|
+
- from: PENDING
|
|
150
|
+
to: CONFIRMED
|
|
151
|
+
method: confirm
|
|
152
|
+
- from: CONFIRMED
|
|
153
|
+
to: SHIPPED
|
|
154
|
+
method: ship
|
|
155
|
+
- from: [PENDING, CONFIRMED]
|
|
156
|
+
to: CANCELLED
|
|
157
|
+
method: cancel
|
|
158
|
+
guard: "this.status == OrderStatus.DELIVERED"
|
|
159
|
+
values: [PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED]
|
|
160
|
+
|
|
161
|
+
events:
|
|
162
|
+
- name: OrderPlacedEvent
|
|
163
|
+
topic: ORDER_PLACED
|
|
164
|
+
triggers:
|
|
165
|
+
- confirm
|
|
166
|
+
fields:
|
|
167
|
+
- name: customerId
|
|
168
|
+
type: String
|
|
169
|
+
- name: confirmedAt
|
|
170
|
+
type: LocalDateTime
|
|
171
|
+
- name: OrderCancelledEvent
|
|
172
|
+
topic: ORDER_CANCELLED
|
|
173
|
+
triggers:
|
|
174
|
+
- cancel
|
|
175
|
+
fields:
|
|
176
|
+
- name: reason
|
|
177
|
+
type: String
|
|
178
|
+
|
|
179
|
+
endpoints:
|
|
180
|
+
basePath: /orders
|
|
181
|
+
versions:
|
|
182
|
+
- version: v1
|
|
183
|
+
operations:
|
|
184
|
+
- useCase: GetOrder
|
|
185
|
+
method: GET
|
|
186
|
+
path: /{id}
|
|
187
|
+
- useCase: FindAllOrders
|
|
188
|
+
method: GET
|
|
189
|
+
path: /
|
|
190
|
+
- useCase: CreateOrder
|
|
191
|
+
method: POST
|
|
192
|
+
path: /
|
|
193
|
+
- useCase: ConfirmOrder
|
|
194
|
+
method: PUT
|
|
195
|
+
path: /{id}/confirm
|
|
196
|
+
|
|
197
|
+
listeners:
|
|
198
|
+
- event: PaymentApprovedEvent
|
|
199
|
+
producer: payments
|
|
200
|
+
topic: PAYMENT_APPROVED
|
|
201
|
+
useCase: ConfirmOrder
|
|
202
|
+
fields:
|
|
203
|
+
- name: orderId
|
|
204
|
+
type: String
|
|
205
|
+
- name: approvedAt
|
|
206
|
+
type: LocalDateTime
|
|
207
|
+
- name: paymentDetails
|
|
208
|
+
type: PaymentDetails
|
|
209
|
+
nestedTypes:
|
|
210
|
+
- name: paymentDetails
|
|
211
|
+
fields:
|
|
212
|
+
- name: paymentId
|
|
213
|
+
type: String
|
|
214
|
+
- name: amount
|
|
215
|
+
type: BigDecimal
|
|
216
|
+
|
|
217
|
+
ports:
|
|
218
|
+
- name: findCustomerById
|
|
219
|
+
service: OrderCustomerService
|
|
220
|
+
target: customers
|
|
221
|
+
baseUrl: http://localhost:8080
|
|
222
|
+
http: GET /customers/{id}
|
|
223
|
+
fields:
|
|
224
|
+
- name: id
|
|
225
|
+
type: String
|
|
226
|
+
- name: email
|
|
227
|
+
type: String
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Visibilidad de campos
|
|
233
|
+
|
|
234
|
+
| Configuración | Business constructor | CreateDto | ResponseDto |
|
|
235
|
+
|---|---|---|---|
|
|
236
|
+
| Normal | ✅ | ✅ | ✅ |
|
|
237
|
+
| `readOnly: true` | ❌ | ❌ | ✅ |
|
|
238
|
+
| `readOnly` + `defaultValue` | ⚡ asignado con default | ❌ | ✅ |
|
|
239
|
+
| `hidden: true` | ✅ | ✅ | ❌ |
|
|
240
|
+
| Ambos flags | ❌ | ❌ | ❌ |
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Tipos de datos soportados
|
|
245
|
+
|
|
246
|
+
| YAML | Java |
|
|
247
|
+
|---|---|
|
|
248
|
+
| `String` | String |
|
|
249
|
+
| `Integer` | Integer |
|
|
250
|
+
| `Long` | Long |
|
|
251
|
+
| `BigDecimal` | BigDecimal |
|
|
252
|
+
| `Boolean` | Boolean |
|
|
253
|
+
| `LocalDate` | LocalDate |
|
|
254
|
+
| `LocalDateTime` | LocalDateTime |
|
|
255
|
+
| `LocalTime` | LocalTime |
|
|
256
|
+
| `Instant` | Instant |
|
|
257
|
+
| `UUID` | UUID |
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Relaciones
|
|
262
|
+
|
|
263
|
+
- ✅ `OneToMany`/`OneToOne` entre entidades del **mismo agregado** → declarar solo en raíz
|
|
264
|
+
- ✅ El generador infiere el lado inverso (`ManyToOne`) — **no declararlo en la secundaria**
|
|
265
|
+
- ✅ Referencia cross-aggregate → `reference:` en campo ID, nunca `relationships:`
|
|
266
|
+
- `mappedBy` debe coincidir con el nombre del campo inverso
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Validaciones JSR-303
|
|
271
|
+
|
|
272
|
+
Declarar en `fields[].validations` — se aplican **únicamente** en Command y CreateDto.
|
|
273
|
+
|
|
274
|
+
```yaml
|
|
275
|
+
validations:
|
|
276
|
+
- type: NotBlank
|
|
277
|
+
message: "Required"
|
|
278
|
+
- type: Email
|
|
279
|
+
message: "Invalid email"
|
|
280
|
+
- type: Size
|
|
281
|
+
min: 3
|
|
282
|
+
max: 50
|
|
283
|
+
- type: Min
|
|
284
|
+
value: 1
|
|
285
|
+
- type: Positive
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Anotaciones: `NotNull`, `NotBlank`, `NotEmpty`, `Email`, `Size`, `Min`, `Max`, `Pattern`, `Digits`, `Positive`, `PositiveOrZero`, `Negative`, `Past`, `Future`, `AssertTrue`, `AssertFalse`.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Proyecciones locales (read models)
|
|
293
|
+
|
|
294
|
+
Agregados sincronizados por listeners Kafka. Señales:
|
|
295
|
+
- Campos coinciden con `listeners[].fields`
|
|
296
|
+
- useCase: `Register*InLocalCatalog`, `Sync*`, `Update*FromEvent`
|
|
297
|
+
- Sin endpoints de escritura
|
|
298
|
+
- Existe para evitar llamadas síncronas en tiempo real
|
|
299
|
+
|
|
300
|
+
**Auditoría:** `audit.enabled: true`, `trackUser: false` (cambios vienen de eventos, no de usuarios).
|
|
301
|
+
|
|
302
|
+
```yaml
|
|
303
|
+
- name: BikeCatalog
|
|
304
|
+
entities:
|
|
305
|
+
- name: bikeCatalogEntry
|
|
306
|
+
isRoot: true
|
|
307
|
+
tableName: bike_catalog_entries
|
|
308
|
+
audit:
|
|
309
|
+
enabled: true
|
|
310
|
+
trackUser: false
|
|
311
|
+
fields:
|
|
312
|
+
- name: id
|
|
313
|
+
type: String
|
|
314
|
+
- name: isAvailable
|
|
315
|
+
type: Boolean
|
|
316
|
+
readOnly: true
|
|
317
|
+
defaultValue: true
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Clasificación de campos readOnly
|
|
323
|
+
|
|
324
|
+
Antes de declarar `events:`, clasifica cada campo `readOnly`:
|
|
325
|
+
|
|
326
|
+
| Categoría | Cuándo se asigna | Debe aparecer en... |
|
|
327
|
+
|---|---|---|
|
|
328
|
+
| Constante del sistema | Constructor con `defaultValue` | Ningún evento |
|
|
329
|
+
| Estado de máquina | Cada transición (enum) | Ningún campo explícito |
|
|
330
|
+
| Timestamp de transición | Una transición específica | El evento de **esa** transición |
|
|
331
|
+
| Dato calculado | Al ejecutar transición | El evento de esa transición |
|
|
332
|
+
| Acumulador | Múltiples operaciones | Eventos de cada operación |
|
|
333
|
+
|
|
334
|
+
**Protocolo para cada campo `readOnly` sin `defaultValue` y sin enum con `transitions`:**
|
|
335
|
+
1. ¿En qué transición se asigna?
|
|
336
|
+
2. ¿Esa transición tiene evento con triggers? → Sí: agregar al `fields[]`; No: crear evento + triggers
|
|
337
|
+
3. Si se asigna en múltiples transiciones: incluir en el evento principal
|
|
338
|
+
|
|
339
|
+
> Transición que asigna campos `readOnly` sin evento asociado = **brecha de diseño**.
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Análisis de activación de transiciones
|
|
344
|
+
|
|
345
|
+
Para cada método en `transitions[]`, clasificar:
|
|
346
|
+
|
|
347
|
+
| Quién activa | Mecanismo correcto |
|
|
348
|
+
|---|---|
|
|
349
|
+
| Endpoint HTTP | `endpoints:` con useCase semántico |
|
|
350
|
+
| Listener Kafka | `listeners:` con useCase semántico |
|
|
351
|
+
| Respuesta exitosa de port | Domain event + `triggers: [método]` |
|
|
352
|
+
| Respuesta error de port | Domain event + `triggers: [método]` |
|
|
353
|
+
| Scheduler / proceso interno | Domain event + `triggers: [método]` |
|
|
354
|
+
|
|
355
|
+
**Patrón port con dos ramas:**
|
|
356
|
+
```yaml
|
|
357
|
+
events:
|
|
358
|
+
- name: NotificationSentEvent
|
|
359
|
+
triggers: [markAsSent]
|
|
360
|
+
fields:
|
|
361
|
+
- name: sentAt
|
|
362
|
+
type: LocalDateTime
|
|
363
|
+
- name: NotificationFailedEvent
|
|
364
|
+
triggers: [markAsFailed]
|
|
365
|
+
fields: []
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Trazabilidad de enums *Type
|
|
371
|
+
|
|
372
|
+
Enums con nombre `*Type` representan clasificaciones. Cada valor debe ser trazable a:
|
|
373
|
+
|
|
374
|
+
| Origen | Traza semántica |
|
|
375
|
+
|---|---|
|
|
376
|
+
| Listener (nombre evento) | Tokens del nombre del evento |
|
|
377
|
+
| Listener (nombre campo) | Tokens del campo |
|
|
378
|
+
| Endpoint (useCase) | Tokens del useCase |
|
|
379
|
+
| Domain event (nombre) | Tokens del evento |
|
|
380
|
+
|
|
381
|
+
Si un valor no es trazable → falta un endpoint o listener en el diseño.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Checklist del domain.yaml por módulo
|
|
386
|
+
|
|
387
|
+
- [ ] Campo `id` en todas las entidades
|
|
388
|
+
- [ ] Solo una entidad `isRoot: true` por agregado
|
|
389
|
+
- [ ] Relaciones solo en raíz; inverso NO declarado
|
|
390
|
+
- [ ] Campos de auditoría NO en `fields:`
|
|
391
|
+
- [ ] `readOnly` con `defaultValue` si hay valor inicial
|
|
392
|
+
- [ ] Enums con lifecycle tienen `initialValue` + `transitions`
|
|
393
|
+
- [ ] Value Objects sin `id`
|
|
394
|
+
- [ ] Referencias cross-aggregate con `reference:`, no `relationships:`
|
|
395
|
+
- [ ] `events[].name` consistente con `integrations.async[]`
|
|
396
|
+
- [ ] `events[].topic` explícito, coincide con `integrations.async[].topic`
|
|
397
|
+
- [ ] `events[].triggers[]` referencia métodos existentes en `transitions[].method`
|
|
398
|
+
- [ ] `{entityName}Id` en `events[].fields` cuando cruza módulos via Kafka
|
|
399
|
+
- [ ] `endpoints:` con estructura `{ basePath, versions }` — no lista plana
|
|
400
|
+
- [ ] `endpoints[].path` relativos al basePath
|
|
401
|
+
- [ ] `listeners[]` para todos los eventos donde este módulo es consumidor
|
|
402
|
+
- [ ] `listeners[].useCase` coincide con `consumers[].useCase`
|
|
403
|
+
- [ ] `listeners[].topic` bare SCREAMING_SNAKE_CASE (sin topicPrefix)
|
|
404
|
+
- [ ] `ports[]` para todas las entradas sync donde `caller = este módulo`
|
|
405
|
+
- [ ] `ports[].baseUrl` solo en primera entrada de cada `service:`
|
|
406
|
+
- [ ] `nestedTypes[]` para campos de tipo objeto en listeners/ports
|
|
407
|
+
- [ ] Transiciones por `ports:` tienen domain event con `triggers`
|
|
408
|
+
- [ ] Cada enum `*Type` valor trazable a listener/endpoint/event
|
|
409
|
+
- [ ] Proyecciones: `audit.enabled: true`, `trackUser: false`
|
|
410
|
+
- [ ] Todo en inglés
|