eva4j 1.0.13 → 1.0.14
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 +51 -9
- package/DOMAIN_YAML_GUIDE.md +150 -0
- package/bin/eva4j.js +31 -1
- package/design-system.md +797 -0
- package/docs/commands/EVALUATE_SYSTEM.md +542 -0
- package/docs/commands/GENERATE_ENTITIES.md +196 -0
- package/docs/commands/INDEX.md +10 -1
- package/examples/domain-endpoints-relations.yaml +353 -0
- package/examples/domain-endpoints-versioned.yaml +144 -0
- package/examples/domain-endpoints.yaml +135 -0
- package/examples/system.yaml +289 -0
- package/package.json +1 -1
- package/src/commands/create.js +6 -3
- package/src/commands/evaluate-system.js +384 -0
- package/src/commands/generate-entities.js +677 -14
- package/src/commands/generate-kafka-event.js +59 -5
- package/src/commands/generate-system.js +243 -0
- package/src/generators/base-generator.js +9 -1
- package/src/utils/naming.js +3 -2
- package/src/utils/system-validator.js +314 -0
- package/src/utils/yaml-to-entity.js +31 -2
- package/templates/aggregate/AggregateRepository.java.ejs +5 -0
- package/templates/aggregate/AggregateRepositoryImpl.java.ejs +9 -0
- package/templates/aggregate/DomainEventHandler.java.ejs +24 -20
- package/templates/aggregate/JpaRepository.java.ejs +5 -0
- package/templates/base/root/skill-build-domain-yaml-references-generate-entities.md.ejs +1103 -0
- package/templates/base/root/skill-build-domain-yaml.ejs +292 -0
- package/templates/base/root/skill-build-system-yaml.ejs +252 -0
- package/templates/base/root/system.yaml.ejs +97 -0
- package/templates/crud/EndpointsController.java.ejs +178 -0
- package/templates/crud/FindByQuery.java.ejs +17 -0
- package/templates/crud/FindByQueryHandler.java.ejs +57 -0
- package/templates/crud/ScaffoldCommand.java.ejs +12 -0
- package/templates/crud/ScaffoldCommandHandler.java.ejs +43 -0
- package/templates/crud/ScaffoldQuery.java.ejs +12 -0
- package/templates/crud/ScaffoldQueryHandler.java.ejs +40 -0
- package/templates/crud/SubEntityAddCommand.java.ejs +17 -0
- package/templates/crud/SubEntityAddCommandHandler.java.ejs +43 -0
- package/templates/crud/SubEntityRemoveCommand.java.ejs +9 -0
- package/templates/crud/SubEntityRemoveCommandHandler.java.ejs +42 -0
- package/templates/crud/TransitionCommand.java.ejs +9 -0
- package/templates/crud/TransitionCommandHandler.java.ejs +39 -0
- package/templates/evaluate/report.html.ejs +971 -0
- package/templates/kafka-event/Event.java.ejs +7 -0
package/design-system.md
ADDED
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
# Design: System-First Development con `system.yaml`
|
|
2
|
+
|
|
3
|
+
> **Estado:** Propuesta en evolución
|
|
4
|
+
> **Versión:** 0.1.0
|
|
5
|
+
> **Fecha:** 2026-03-09
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 📋 Tabla de Contenidos
|
|
10
|
+
|
|
11
|
+
- [Visión](#visión)
|
|
12
|
+
- [Motivación](#motivación)
|
|
13
|
+
- [Principio Central](#principio-central)
|
|
14
|
+
- [Los Dos Artefactos](#los-dos-artefactos)
|
|
15
|
+
- [Anatomía de system.yaml](#anatomía-de-systemyaml)
|
|
16
|
+
- [Puertos Secundarios en domain.yaml](#puertos-secundarios-en-domainyaml)
|
|
17
|
+
- [Relación entre los Archivos](#relación-entre-los-archivos)
|
|
18
|
+
- [Flujo de Trabajo](#flujo-de-trabajo)
|
|
19
|
+
- [Comandos Nuevos](#comandos-nuevos)
|
|
20
|
+
- [Validaciones de system.yaml](#validaciones-de-systemyaml)
|
|
21
|
+
- [Aspectos Positivos del Enfoque](#aspectos-positivos-del-enfoque)
|
|
22
|
+
- [Colaboración con Agentes de IA](#colaboración-con-agentes-de-ia)
|
|
23
|
+
- [Cómo Construir el system.yaml con un Agente de IA](#cómo-construir-el-systemyaml-con-un-agente-de-ia)
|
|
24
|
+
- [Preguntas Abiertas](#preguntas-abiertas)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Visión
|
|
29
|
+
|
|
30
|
+
> Construir sistemas backend robustos, con calidad de arquitectura enterprise, en una fracción del tiempo tradicional — iterando a la velocidad del negocio, no del código.
|
|
31
|
+
|
|
32
|
+
El objetivo final de este enfoque es habilitar un **ciclo de desarrollo acelerado por IA** donde:
|
|
33
|
+
|
|
34
|
+
- El equipo opera permanentemente en el nivel de **intención y negocio**, no en el nivel de código
|
|
35
|
+
- Los patrones arquitecturales correctos (hexagonal, DDD, CQRS) son **garantizados por el tooling**, no por la disciplina individual
|
|
36
|
+
- La IA amplifica la capacidad del equipo sin introducir inconsistencias
|
|
37
|
+
- Cada iteración es **quirúrgica y predecible**: cambiar una regla de negocio no requiere arqueología de código
|
|
38
|
+
|
|
39
|
+
### El ciclo fundamental
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
43
|
+
│ │
|
|
44
|
+
│ INTENCIÓN ESPECIFICACIÓN CÓDIGO │
|
|
45
|
+
│ (negocio) → (YAML) → (Java) │
|
|
46
|
+
│ ↑ │
|
|
47
|
+
│ │ revisión humana │
|
|
48
|
+
│ │ (diff de YAML, │
|
|
49
|
+
│ │ no de código) │
|
|
50
|
+
│ │
|
|
51
|
+
│ ◄────── iteración ────────────────── │
|
|
52
|
+
└─────────────────────────────────────────────────────────────┘
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Por qué este ciclo es cualitativamente diferente
|
|
56
|
+
|
|
57
|
+
| Enfoque | Velocidad | Consistencia | Revisión humana | Iteración |
|
|
58
|
+
|---|---|---|---|---|
|
|
59
|
+
| Tradicional | Lenta | Depende del equipo | Revisión de código (costosa) | Lenta |
|
|
60
|
+
| AI genera código directamente | Rápida | Baja — AI improvisa patrones | Difícil — código generado es opaco | Frágil |
|
|
61
|
+
| **System-First + eva4j + AI** | **Muy rápida** | **Alta — patrones garantizados por el generador** | **Fácil — se revisan YAMLs, no código** | **Rápida y segura** |
|
|
62
|
+
|
|
63
|
+
La clave es que **la IA y el humano colaboran en el nivel de especificación**, no en el nivel de código. El código Java es siempre un artefacto generado, determinista y consistente. Nadie necesita revisar si el mapper excluye los campos de auditoría — eva4j siempre lo hace bien.
|
|
64
|
+
|
|
65
|
+
### La promesa concreta
|
|
66
|
+
|
|
67
|
+
Un sistema con 5-8 módulos, con comunicación asíncrona (Kafka) y síncrona (HTTP), con arquitectura hexagonal completa y cobertura de casos de uso principales:
|
|
68
|
+
|
|
69
|
+
- **Hoy (sin este enfoque):** semanas de desarrollo + semanas de revisión arquitectural
|
|
70
|
+
- **Con este enfoque:** días para la especificación colaborativa + horas para la generación y validación
|
|
71
|
+
|
|
72
|
+
La ganancia no viene de generar código más rápido — viene de **eliminar las decisiones repetitivas** (¿cómo nombro este mapper?, ¿dónde va esta clase?, ¿cómo estructuro este evento?) y dejar al equipo enfocado en las decisiones que realmente importan: qué reglas de negocio son correctas, qué contratos entre módulos tienen sentido, qué comportamientos del dominio necesitan transiciones de estado.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Motivación
|
|
77
|
+
|
|
78
|
+
Hoy en eva4j el punto de entrada es el módulo: se crea uno a uno, se diseña su `domain.yaml` de forma aislada, y la comunicación entre módulos se configura de forma imperativa e interactiva mediante comandos como `eva g kafka-event`, `eva g kafka-listener` y `eva g http-exchange`.
|
|
79
|
+
|
|
80
|
+
Esto funciona bien para módulos individuales, pero deja una brecha: **no existe ningún artefacto que describa el sistema como un todo** — qué módulos existen, cómo se comunican, y cuáles son los contratos entre ellos.
|
|
81
|
+
|
|
82
|
+
La propuesta introduce un enfoque **System-First**: el diseño comienza definiendo la arquitectura del sistema en un único archivo de alto nivel (`system.yaml`), a partir del cual se hace bootstrap de toda la estructura.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Principio Central
|
|
87
|
+
|
|
88
|
+
> El `system.yaml` describe **qué módulos existen y cómo se comunican**.
|
|
89
|
+
> El `domain.yaml` describe **qué es el dominio de cada módulo y qué puertos secundarios necesita**.
|
|
90
|
+
|
|
91
|
+
Los dos archivos son independientes y evolucionan en momentos distintos, pero tienen una relación de coherencia que eva4j puede validar.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Los Dos Artefactos
|
|
96
|
+
|
|
97
|
+
| Archivo | Nivel | Responde a | Lo define |
|
|
98
|
+
|---|---|---|---|
|
|
99
|
+
| `system.yaml` | Sistema | ¿Qué módulos existen? ¿Qué fluye entre ellos? ¿Qué exponen? | Arquitecto / diseñador del sistema |
|
|
100
|
+
| `domain.yaml` | Módulo | ¿Qué entidades, reglas, eventos y puertos secundarios tiene este módulo? | Desarrollador del módulo |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Anatomía de `system.yaml`
|
|
105
|
+
|
|
106
|
+
El archivo vive en la **raíz del proyecto** y tiene tres secciones:
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
# system.yaml
|
|
110
|
+
|
|
111
|
+
system:
|
|
112
|
+
name: ecommerce-platform
|
|
113
|
+
groupId: com.acme
|
|
114
|
+
javaVersion: 21
|
|
115
|
+
springBootVersion: 3.4.1
|
|
116
|
+
database: postgresql
|
|
117
|
+
|
|
118
|
+
messaging:
|
|
119
|
+
enabled: true
|
|
120
|
+
broker: kafka # kafka | rabbitmq | sns-sqs (solo kafka soportado actualmente)
|
|
121
|
+
kafka:
|
|
122
|
+
bootstrapServers: localhost:9092
|
|
123
|
+
defaultGroupId: ecommerce-platform
|
|
124
|
+
topicPrefix: ecommerce # opcional — prefixa todos los topics: ecommerce.ORDER_PLACED
|
|
125
|
+
|
|
126
|
+
modules:
|
|
127
|
+
- name: orders
|
|
128
|
+
description: "Gestión del ciclo de vida de pedidos"
|
|
129
|
+
exposes:
|
|
130
|
+
- method: GET
|
|
131
|
+
path: /orders/{id}
|
|
132
|
+
useCase: GetOrder
|
|
133
|
+
description: "Obtener detalle de un pedido"
|
|
134
|
+
- method: GET
|
|
135
|
+
path: /orders
|
|
136
|
+
useCase: FindAllOrders
|
|
137
|
+
description: "Listar pedidos con filtros y paginación"
|
|
138
|
+
- method: POST
|
|
139
|
+
path: /orders
|
|
140
|
+
useCase: CreateOrder
|
|
141
|
+
description: "Crear nuevo pedido"
|
|
142
|
+
- method: PUT
|
|
143
|
+
path: /orders/{id}/confirm
|
|
144
|
+
useCase: ConfirmOrder
|
|
145
|
+
description: "Confirmar pedido pendiente"
|
|
146
|
+
- method: PUT
|
|
147
|
+
path: /orders/{id}/cancel
|
|
148
|
+
useCase: CancelOrder
|
|
149
|
+
description: "Cancelar pedido (PENDING o CONFIRMED)"
|
|
150
|
+
|
|
151
|
+
- name: customers
|
|
152
|
+
description: "Registro y gestión de clientes"
|
|
153
|
+
exposes:
|
|
154
|
+
- method: GET
|
|
155
|
+
path: /customers/{id}
|
|
156
|
+
useCase: GetCustomer
|
|
157
|
+
description: "Obtener cliente por ID"
|
|
158
|
+
- method: GET
|
|
159
|
+
path: /customers
|
|
160
|
+
useCase: FindAllCustomers
|
|
161
|
+
description: "Listar clientes con filtros"
|
|
162
|
+
- method: POST
|
|
163
|
+
path: /customers
|
|
164
|
+
useCase: CreateCustomer
|
|
165
|
+
description: "Registrar nuevo cliente"
|
|
166
|
+
- method: PUT
|
|
167
|
+
path: /customers/{id}
|
|
168
|
+
useCase: UpdateCustomer
|
|
169
|
+
description: "Actualizar datos del cliente"
|
|
170
|
+
|
|
171
|
+
- name: payments
|
|
172
|
+
description: "Procesamiento de pagos"
|
|
173
|
+
exposes:
|
|
174
|
+
- method: POST
|
|
175
|
+
path: /payments
|
|
176
|
+
useCase: CreatePayment
|
|
177
|
+
description: "Iniciar procesamiento de pago"
|
|
178
|
+
- method: GET
|
|
179
|
+
path: /payments/{id}
|
|
180
|
+
useCase: GetPayment
|
|
181
|
+
description: "Consultar estado de un pago"
|
|
182
|
+
- method: POST
|
|
183
|
+
path: /payments/{id}/refund
|
|
184
|
+
useCase: RefundPayment
|
|
185
|
+
description: "Solicitar reembolso"
|
|
186
|
+
|
|
187
|
+
- name: notifications
|
|
188
|
+
description: "Envío de notificaciones"
|
|
189
|
+
# Sin endpoints REST — solo consume eventos
|
|
190
|
+
|
|
191
|
+
integrations:
|
|
192
|
+
async:
|
|
193
|
+
- event: OrderPlacedEvent
|
|
194
|
+
producer: orders
|
|
195
|
+
topic: ORDER_PLACED
|
|
196
|
+
consumers:
|
|
197
|
+
- module: payments
|
|
198
|
+
- module: notifications
|
|
199
|
+
|
|
200
|
+
- event: OrderCancelledEvent
|
|
201
|
+
producer: orders
|
|
202
|
+
topic: ORDER_CANCELLED
|
|
203
|
+
consumers:
|
|
204
|
+
- module: payments
|
|
205
|
+
- module: notifications
|
|
206
|
+
|
|
207
|
+
- event: PaymentProcessedEvent
|
|
208
|
+
producer: payments
|
|
209
|
+
topic: PAYMENT_PROCESSED
|
|
210
|
+
consumers:
|
|
211
|
+
- module: orders
|
|
212
|
+
|
|
213
|
+
sync:
|
|
214
|
+
- caller: orders
|
|
215
|
+
calls: customers
|
|
216
|
+
port: CustomerService
|
|
217
|
+
using:
|
|
218
|
+
- GET /customers/{id}
|
|
219
|
+
|
|
220
|
+
- caller: payments
|
|
221
|
+
calls: orders
|
|
222
|
+
port: OrderService
|
|
223
|
+
using:
|
|
224
|
+
- GET /orders/{id}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Reglas del `system.yaml`
|
|
228
|
+
|
|
229
|
+
- Solo describe **qué existe** y **qué fluye** — no sabe nada de entidades, campos ni lógica de negocio
|
|
230
|
+
- Los endpoints en `exposes:` usan sintaxis objeto: `method`, `path`, `useCase` (obligatorio) y `description`
|
|
231
|
+
- El campo `useCase` permite a `eva generate system` pre-generar la sección `endpoints:` completa en `domain.yaml`
|
|
232
|
+
- Los endpoints en `exposes:` sirven también para validar los `calls.using:`
|
|
233
|
+
- Los módulos en `consumers:` deben existir en `modules:`
|
|
234
|
+
- Los eventos en `consumers:` deben tener exactamente un `producer:`
|
|
235
|
+
|
|
236
|
+
### Sección `messaging`
|
|
237
|
+
|
|
238
|
+
| Campo | Obligatorio | Descripción |
|
|
239
|
+
|---|---|---|
|
|
240
|
+
| `enabled` | sí | `true` para activar soporte de mensajería asíncrona |
|
|
241
|
+
| `broker` | sí | Tipo de broker: `kafka` \| `rabbitmq` \| `sns-sqs` |
|
|
242
|
+
| `kafka.bootstrapServers` | cuando `broker: kafka` | Host(s) del broker Kafka |
|
|
243
|
+
| `kafka.defaultGroupId` | no | Consumer group ID base; cada módulo añade su sufijo |
|
|
244
|
+
| `kafka.topicPrefix` | no | Prefijo global para todos los topics del sistema |
|
|
245
|
+
| `rabbitmq.host` | cuando `broker: rabbitmq` | Host del broker RabbitMQ |
|
|
246
|
+
| `rabbitmq.port` | no | Puerto (default `5672`) |
|
|
247
|
+
| `rabbitmq.virtualHost` | no | VirtualHost (default `/`) |
|
|
248
|
+
| `rabbitmq.exchangeType` | no | Tipo de exchange: `topic` \| `direct` \| `fanout` (default `topic`) |
|
|
249
|
+
| `sns-sqs.region` | cuando `broker: sns-sqs` | Región AWS |
|
|
250
|
+
| `sns-sqs.accountId` | cuando `broker: sns-sqs` | AWS Account ID (para construir ARNs) |
|
|
251
|
+
| `sns-sqs.endpointOverride` | no | URL local para desarrollo (ej. LocalStack) |
|
|
252
|
+
|
|
253
|
+
> **Nota:** solo `kafka` está soportado actualmente. Los valores `rabbitmq` y `sns-sqs` están reservados para versiones futuras y generan un warning al ejecutar `eva system validate`.
|
|
254
|
+
|
|
255
|
+
#### Ejemplo con RabbitMQ
|
|
256
|
+
|
|
257
|
+
En brokers basados en colas, el modelo de comunicación cambia: en lugar de **topics** (Kafka) se usan **exchanges + queues** (RabbitMQ) o **topics SNS + colas SQS** (AWS). La integración sigue siendo declarativa — la diferencia está en los campos de configuración y en cómo se nombran los canales en `integrations.async`.
|
|
258
|
+
|
|
259
|
+
```yaml
|
|
260
|
+
# system.yaml — broker RabbitMQ
|
|
261
|
+
|
|
262
|
+
system:
|
|
263
|
+
name: ecommerce-platform
|
|
264
|
+
groupId: com.acme
|
|
265
|
+
javaVersion: 21
|
|
266
|
+
springBootVersion: 3.4.1
|
|
267
|
+
database: postgresql
|
|
268
|
+
|
|
269
|
+
messaging:
|
|
270
|
+
enabled: true
|
|
271
|
+
broker: rabbitmq
|
|
272
|
+
rabbitmq:
|
|
273
|
+
host: localhost
|
|
274
|
+
port: 5672
|
|
275
|
+
virtualHost: /ecommerce
|
|
276
|
+
exchangeType: topic # un exchange por evento (topic exchange)
|
|
277
|
+
|
|
278
|
+
modules:
|
|
279
|
+
- name: orders
|
|
280
|
+
description: "Gestión del ciclo de vida de pedidos"
|
|
281
|
+
exposes:
|
|
282
|
+
- method: POST
|
|
283
|
+
path: /orders
|
|
284
|
+
useCase: CreateOrder
|
|
285
|
+
description: "Crear nuevo pedido"
|
|
286
|
+
- method: PUT
|
|
287
|
+
path: /orders/{id}/confirm
|
|
288
|
+
useCase: ConfirmOrder
|
|
289
|
+
description: "Confirmar pedido pendiente"
|
|
290
|
+
- method: PUT
|
|
291
|
+
path: /orders/{id}/cancel
|
|
292
|
+
useCase: CancelOrder
|
|
293
|
+
description: "Cancelar pedido"
|
|
294
|
+
|
|
295
|
+
- name: payments
|
|
296
|
+
description: "Procesamiento de pagos"
|
|
297
|
+
exposes:
|
|
298
|
+
- method: POST
|
|
299
|
+
path: /payments
|
|
300
|
+
useCase: CreatePayment
|
|
301
|
+
description: "Iniciar procesamiento de pago"
|
|
302
|
+
|
|
303
|
+
- name: notifications
|
|
304
|
+
description: "Envío de notificaciones"
|
|
305
|
+
# Sin endpoints REST — solo consume eventos
|
|
306
|
+
|
|
307
|
+
integrations:
|
|
308
|
+
async:
|
|
309
|
+
- event: OrderPlacedEvent
|
|
310
|
+
producer: orders
|
|
311
|
+
exchange: orders.events # exchange RabbitMQ
|
|
312
|
+
routingKey: order.placed # routing key del mensaje
|
|
313
|
+
consumers:
|
|
314
|
+
- module: payments
|
|
315
|
+
queue: payments.order.placed # cola dedicada por consumidor
|
|
316
|
+
- module: notifications
|
|
317
|
+
queue: notifications.order.placed
|
|
318
|
+
|
|
319
|
+
- event: PaymentProcessedEvent
|
|
320
|
+
producer: payments
|
|
321
|
+
exchange: payments.events
|
|
322
|
+
routingKey: payment.processed
|
|
323
|
+
consumers:
|
|
324
|
+
- module: orders
|
|
325
|
+
queue: orders.payment.processed
|
|
326
|
+
|
|
327
|
+
sync:
|
|
328
|
+
- caller: orders
|
|
329
|
+
calls: customers
|
|
330
|
+
port: CustomerService
|
|
331
|
+
using:
|
|
332
|
+
- GET /customers/{id}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Diferencias clave respecto a Kafka:**
|
|
336
|
+
|
|
337
|
+
| Concepto | Kafka | RabbitMQ |
|
|
338
|
+
|---|---|---|
|
|
339
|
+
| Canal de publicación | `topic` | `exchange` + `routingKey` |
|
|
340
|
+
| Canal de consumo | `topic` (compartido) | `queue` (exclusiva por consumidor) |
|
|
341
|
+
| Retención de mensajes | Log persistente (configurable) | Hasta que el consumidor los acepta |
|
|
342
|
+
| Fan-out | Un topic, múltiples consumer groups | Un exchange, múltiples queues binding |
|
|
343
|
+
| Replay | Sí (offset reset) | No (requiere DLQ + republicación) |
|
|
344
|
+
|
|
345
|
+
#### Ejemplo con SNS/SQS
|
|
346
|
+
|
|
347
|
+
```yaml
|
|
348
|
+
# system.yaml — broker SNS/SQS (AWS)
|
|
349
|
+
|
|
350
|
+
messaging:
|
|
351
|
+
enabled: true
|
|
352
|
+
broker: sns-sqs
|
|
353
|
+
sns-sqs:
|
|
354
|
+
region: us-east-1
|
|
355
|
+
accountId: "123456789012"
|
|
356
|
+
endpointOverride: http://localhost:4566 # LocalStack para desarrollo local
|
|
357
|
+
|
|
358
|
+
integrations:
|
|
359
|
+
async:
|
|
360
|
+
- event: OrderPlacedEvent
|
|
361
|
+
producer: orders
|
|
362
|
+
topic: arn:aws:sns:us-east-1:123456789012:OrderPlaced # ARN del topic SNS
|
|
363
|
+
consumers:
|
|
364
|
+
- module: payments
|
|
365
|
+
queue: arn:aws:sqs:us-east-1:123456789012:payments-order-placed
|
|
366
|
+
- module: notifications
|
|
367
|
+
queue: arn:aws:sqs:us-east-1:123456789012:notifications-order-placed
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Puertos Secundarios en `domain.yaml`
|
|
373
|
+
|
|
374
|
+
Los **puertos secundarios** son interfaces del dominio que representan dependencias hacia otros módulos. Al ser propiedad del dominio en arquitectura hexagonal, se declaran en `domain.yaml` bajo la sección `ports:`:
|
|
375
|
+
|
|
376
|
+
```yaml
|
|
377
|
+
# orders/domain.yaml (fragmento)
|
|
378
|
+
aggregates:
|
|
379
|
+
- name: Order
|
|
380
|
+
entities: [...]
|
|
381
|
+
events:
|
|
382
|
+
- name: OrderPlacedEvent
|
|
383
|
+
fields:
|
|
384
|
+
- name: orderId
|
|
385
|
+
type: String
|
|
386
|
+
- name: customerId
|
|
387
|
+
type: String
|
|
388
|
+
kafka: true
|
|
389
|
+
|
|
390
|
+
ports: # puertos secundarios del módulo
|
|
391
|
+
- name: CustomerService
|
|
392
|
+
target: customers # módulo destino — validado contra system.yaml modules:
|
|
393
|
+
methods:
|
|
394
|
+
- name: findCustomerById
|
|
395
|
+
http: GET /customers/{id}
|
|
396
|
+
response:
|
|
397
|
+
- name: id
|
|
398
|
+
type: String
|
|
399
|
+
- name: fullName
|
|
400
|
+
type: String
|
|
401
|
+
- name: email
|
|
402
|
+
type: String
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Por qué `ports:` pertenece al dominio
|
|
406
|
+
|
|
407
|
+
En arquitectura hexagonal, la **interfaz del puerto secundario es propiedad del dominio** — el dominio define qué necesita, la infraestructura decide cómo obtenerlo (Feign, RestTemplate, stub). Declararlos en `domain.yaml` es semánticamente correcto y consistente con el precedente establecido por `reference:` en los campos.
|
|
408
|
+
|
|
409
|
+
### Lo que `eva g entities` genera a partir de `ports:`
|
|
410
|
+
|
|
411
|
+
- **Interfaz del puerto** en `domain/repositories/` → `CustomerService.java`
|
|
412
|
+
- **DTO de respuesta local** en `application/dtos/` → `CustomerDto.java`
|
|
413
|
+
- **Implementación Feign** en `infrastructure/adapters/` → `CustomerServiceFeignAdapter.java`
|
|
414
|
+
|
|
415
|
+
### Validación cruzada
|
|
416
|
+
|
|
417
|
+
> `ports[].target` en `domain.yaml` **debe existir** en `system.yaml → modules`.
|
|
418
|
+
> `eva system validate` detecta referencias rotas entre módulos.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Relación entre los Archivos
|
|
423
|
+
|
|
424
|
+
### Dependencias de datos
|
|
425
|
+
|
|
426
|
+
```
|
|
427
|
+
system.yaml
|
|
428
|
+
│
|
|
429
|
+
└─── genera bootstrap de ──► domain.yaml (esqueleto con endpoints: pre-generado)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
domain.yaml (events[] + ports[])
|
|
433
|
+
│
|
|
434
|
+
└─── genera código de ──────► eva g entities <module>
|
|
435
|
+
→ entidades, repos, mappers, kafka events, feign clients
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Dependencia dura validable
|
|
439
|
+
|
|
440
|
+
> `ports[].target` en `domain.yaml` **debe existir** en `system.yaml → modules`.
|
|
441
|
+
> `eva system validate` detecta referencias rotas entre módulos.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Flujo de Trabajo
|
|
446
|
+
|
|
447
|
+
```
|
|
448
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
449
|
+
│ 1. DISEÑO ARQUITECTURAL │
|
|
450
|
+
│ │
|
|
451
|
+
│ [Definir/editar system.yaml] │
|
|
452
|
+
│ eva system validate → detecta inconsistencias en el grafo │
|
|
453
|
+
└──────────────────────────────────────┬──────────────────────────────────┘
|
|
454
|
+
│
|
|
455
|
+
▼
|
|
456
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
457
|
+
│ 2. BOOTSTRAP │
|
|
458
|
+
│ │
|
|
459
|
+
│ eva generate system │
|
|
460
|
+
│ → proyecto base (build.gradle, Application.java, shared/) │
|
|
461
|
+
│ → módulo vacío por cada entrada en modules: │
|
|
462
|
+
│ → domain.yaml esqueleto con endpoints: pre-generado (del system.yaml) │
|
|
463
|
+
│ │
|
|
464
|
+
│ (re-ejecutable) agrega módulos nuevos declarados en system.yaml │
|
|
465
|
+
│ que aún no existen en el proyecto │
|
|
466
|
+
└──────────────────────────────────────┬──────────────────────────────────┘
|
|
467
|
+
│
|
|
468
|
+
▼
|
|
469
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
470
|
+
│ 3. MODELADO DE DOMINIO (por módulo, de forma independiente) │
|
|
471
|
+
│ │
|
|
472
|
+
│ [Editar orders/domain.yaml] → aggregates + events + ports: │
|
|
473
|
+
│ [Editar payments/domain.yaml] → aggregates + events + ports: │
|
|
474
|
+
│ [Editar customers/domain.yaml] → aggregates + events │
|
|
475
|
+
└──────────────────────────────────────┬──────────────────────────────────┘
|
|
476
|
+
│
|
|
477
|
+
▼
|
|
478
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
479
|
+
│ 4. GENERACIÓN DE CÓDIGO │
|
|
480
|
+
│ │
|
|
481
|
+
│ eva g entities orders → entidades, repos, mappers, │
|
|
482
|
+
│ kafka events, feign clients (ports:) │
|
|
483
|
+
│ eva g entities payments │
|
|
484
|
+
│ eva g entities customers │
|
|
485
|
+
│ eva system diagram → diagrama Mermaid del sistema │
|
|
486
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## Comandos Nuevos
|
|
492
|
+
|
|
493
|
+
| Comando | Descripción |
|
|
494
|
+
|---|---|
|
|
495
|
+
| `eva system validate` | Valida coherencia del `system.yaml` (referencias rotas, ciclos, eventos sin consumidor, `ports[].target` inexistentes) |
|
|
496
|
+
| `eva generate system` | Bootstrap completo: proyecto + módulos + `domain.yaml` esqueleto con `endpoints:` pre-generado |
|
|
497
|
+
| `eva system diagram` | Genera diagrama Mermaid del grafo de módulos y comunicaciones |
|
|
498
|
+
|
|
499
|
+
### Relación con comandos existentes
|
|
500
|
+
|
|
501
|
+
Los comandos actuales (`eva g kafka-event`, `eva g kafka-listener`, `eva g http-exchange`) seguirían funcionando para casos puntuales. El comando `eva g entities <module>` ahora también genera el código de infraestructura derivado de `ports:` (feign clients) y `events[]` (kafka producers/consumers), sin prompts interactivos.
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## Validaciones de `system.yaml`
|
|
506
|
+
|
|
507
|
+
`eva system validate` detectaría los siguientes problemas:
|
|
508
|
+
|
|
509
|
+
| Tipo | Ejemplo |
|
|
510
|
+
|---|---|
|
|
511
|
+
| Módulo inexistente en consumidor | `consumers[].module: inventario` pero `inventario` no está en `modules:` |
|
|
512
|
+
| Evento consumido sin productor | `consumes` un `StockUpdatedEvent` que ningún módulo publica |
|
|
513
|
+
| Endpoint referenciado no expuesto | `calls.using: GET /customers/profile` pero `customers` no lo declara en `exposes:` |
|
|
514
|
+
| Dependencia circular síncrona | `orders` llama a `payments` y `payments` llama a `orders` |
|
|
515
|
+
| Puerto con módulo inexistente | `domain.yaml → ports[].target: inventario` pero `inventario` no está en `system.yaml → modules:` |
|
|
516
|
+
| Evento sin consumidores | Un evento publicado que nadie consume (advertencia, no error) |
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
## Aspectos Positivos del Enfoque
|
|
521
|
+
|
|
522
|
+
### 1. Diseño antes de código
|
|
523
|
+
El equipo puede definir y discutir la arquitectura del sistema completo en un solo archivo de texto antes de escribir una sola línea de código Java. El `system.yaml` es legible por cualquier miembro del equipo, incluso sin conocimiento técnico profundo.
|
|
524
|
+
|
|
525
|
+
### 2. Single source of truth arquitectural
|
|
526
|
+
Toda la topología del sistema — qué módulos existen, qué publican, qué consumen, a quién llaman — vive en un único lugar. Elimina la necesidad de diagramas de arquitectura que quedan desactualizados.
|
|
527
|
+
|
|
528
|
+
### 3. Eliminación de prompts interactivos
|
|
529
|
+
Los comandos actuales (`eva g kafka-event`, `eva g kafka-listener`, `eva g http-exchange`) requieren responder preguntas cada vez que se ejecutan. Con `domain.yaml → events[]` y `ports:`, `eva g entities <module>` genera todo (kafka producers, listeners, feign clients) de una vez, sin interacción, reproducible y apto para CI/CD.
|
|
530
|
+
|
|
531
|
+
### 4. Contratos explícitos entre módulos
|
|
532
|
+
Los `events[]` en `domain.yaml` del productor definen el contrato completo del evento. Los `ports:` en `domain.yaml` del consumidor declaran explícitamente qué campos del servicio remoto necesita. Esto hace visibles las dependencias de datos entre módulos y reduce el acoplamiento implícito.
|
|
533
|
+
|
|
534
|
+
### 5. Detección temprana de inconsistencias
|
|
535
|
+
`eva system validate` detecta problemas de arquitectura (referencias rotas, dependencias circulares, contratos mal definidos) antes de generar código y antes de desplegar, cuando el costo de corregirlos es mínimo.
|
|
536
|
+
|
|
537
|
+
### 6. Escalabilidad del proceso
|
|
538
|
+
El mismo flujo funciona para un sistema con 3 módulos o con 30. El `system.yaml` crece linealmente y sigue siendo el mapa del sistema.
|
|
539
|
+
|
|
540
|
+
### 7. Separación de responsabilidades clara
|
|
541
|
+
Cada archivo tiene una responsabilidad única y bien definida:
|
|
542
|
+
- `system.yaml` → **qué existe** (arquitectura — módulos y comunicación)
|
|
543
|
+
- `domain.yaml` → **qué es y qué necesita** (negocio + puertos secundarios)
|
|
544
|
+
|
|
545
|
+
Un cambio de dominio no toca `system.yaml`. Un cambio de arquitectura no toca `domain.yaml`. Las responsabilidades no se mezclan.
|
|
546
|
+
|
|
547
|
+
### 8. Diseño iterativo del sistema
|
|
548
|
+
El `system.yaml` no es solo un artefacto de arranque — **evoluciona con el proyecto**. Es normal que durante el desarrollo emerjan nuevos módulos, endpoints o integraciones que no se vieron inicialmente. Cada iteración sobre `system.yaml` sigue el mismo ciclo ligero: editar → `eva system validate` → actualizar `domain.yaml` afectado → `eva g entities`. El costo de cambiar la arquitectura permanece bajo porque la revisión ocurre en YAML, no en código.
|
|
549
|
+
|
|
550
|
+
### 9. Diagrama siempre actualizado
|
|
551
|
+
`eva system diagram` genera un diagrama Mermaid directamente desde `system.yaml`, garantizando que la documentación visual del sistema nunca queda desactualizada respecto al código.
|
|
552
|
+
|
|
553
|
+
### 10. Preparación natural para microservicios
|
|
554
|
+
El comando `eva detach <module>` (ya existente) se vuelve más potente porque el `system.yaml` ya documenta exactamente qué contratos expone el módulo y con quién se comunica — toda la información necesaria para extraerlo como servicio independiente.
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## Colaboración con Agentes de IA
|
|
559
|
+
|
|
560
|
+
### El problema actual con AI + generación de código
|
|
561
|
+
|
|
562
|
+
Un agente de IA hoy necesita responder preguntas como: ¿qué hace este módulo?, ¿con quién se comunica?, ¿qué eventos publica o consume?, ¿qué ya existe en el sistema? Sin `system.yaml`, el agente tiene que **inferir** todo eso leyendo código, archivos de configuración dispersos y documentación posiblemente desactualizada. El contexto es ruidoso, incompleto y costoso en tokens.
|
|
563
|
+
|
|
564
|
+
### Los YAMLs como contexto perfecto para un agente
|
|
565
|
+
|
|
566
|
+
Los dos archivos juntos son **densos en significado y mínimos en ruido**. Un agente puede leerlos y tener comprensión completa de un módulo en ~100 líneas de YAML, en lugar de miles de líneas de Java:
|
|
567
|
+
|
|
568
|
+
```
|
|
569
|
+
system.yaml → "qué soy dentro del sistema y con quién hablo"
|
|
570
|
+
domain.yaml → "qué reglas de negocio tengo y qué necesito de otros módulos"
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### División natural de trabajo humano-agente
|
|
574
|
+
|
|
575
|
+
El enfoque habilita una colaboración por capas donde el humano opera en el nivel de **intención** y el agente en el nivel de **estructura y detalle**:
|
|
576
|
+
|
|
577
|
+
| Humano | Agente de IA |
|
|
578
|
+
|---|---|
|
|
579
|
+
| Define `system.yaml` (visión arquitectural) | Valida coherencia del grafo, detecta dependencias circulares, sugiere módulos faltantes |
|
|
580
|
+
| Revisa y aprueba | Genera `domain.yaml` de cada módulo (entidades, campos, relaciones, enums, eventos) |
|
|
581
|
+
| Refina `domain.yaml` (ajusta reglas de negocio y puertos) | Completa `domain.yaml` con `ports:` y ajusta `events[]` |
|
|
582
|
+
| Revisa y aprueba | Ejecuta `eva g entities` por módulo — código completo listo para compilar |
|
|
583
|
+
|
|
584
|
+
### Instrucciones precisas y verificables para el agente
|
|
585
|
+
|
|
586
|
+
Con el enfoque YAML-first el agente no infiere — **ejecuta sobre especificación explícita**:
|
|
587
|
+
|
|
588
|
+
- El `system.yaml` le dice el **contrato** que debe respetar
|
|
589
|
+
- El `AGENTS.md` le dice los **patrones** que debe seguir
|
|
590
|
+
- El `domain.yaml` le dice **exactamente qué generar**
|
|
591
|
+
|
|
592
|
+
El resultado es predecible, revisable y consistente entre iteraciones.
|
|
593
|
+
|
|
594
|
+
### Iteración quirúrgica sin regenerar todo
|
|
595
|
+
|
|
596
|
+
El modelo YAML como fuente de verdad hace que cada cambio sea mínimo y rastreable:
|
|
597
|
+
|
|
598
|
+
```
|
|
599
|
+
Cambio de negocio: editar domain.yaml → eva g entities orders
|
|
600
|
+
Nuevo evento: editar system.yaml → eva system validate
|
|
601
|
+
editar domain.yaml → eva g entities orders payments
|
|
602
|
+
Nuevo módulo: editar system.yaml → eva system validate
|
|
603
|
+
eva generate system → genera el módulo nuevo
|
|
604
|
+
diseñar domain.yaml → eva g entities <nuevo-modulo>
|
|
605
|
+
Nuevo endpoint: editar system.yaml (exposes:) → eva system validate
|
|
606
|
+
editar domain.yaml (endpoints:) → eva g entities orders
|
|
607
|
+
Nueva feature completa: agente recibe domain.yaml actual + descripción del cambio
|
|
608
|
+
propone domain.yaml actualizado (diff mínimo)
|
|
609
|
+
humano aprueba → eva g entities
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
Cada iteración tiene un **artefacto de revisión claro** (el YAML diff) antes de que se toque una línea de código Java.
|
|
613
|
+
|
|
614
|
+
### Los YAMLs como sistema de conocimiento del proyecto
|
|
615
|
+
|
|
616
|
+
Combinados, los cuatro archivos forman el contexto completo para cualquier agente que se incorpore al proyecto:
|
|
617
|
+
|
|
618
|
+
```
|
|
619
|
+
AGENTS.md → "cómo se hace en eva4j" (patrones globales)
|
|
620
|
+
system.yaml → "qué existe en este proyecto" (topología del sistema)
|
|
621
|
+
domain.yaml → "qué es este módulo y cómo habla" (negocio + puertos)
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
Un agente que recibe estos tres archivos tiene todo lo que necesita para contribuir al proyecto **sin sesión de onboarding**.
|
|
625
|
+
|
|
626
|
+
### Generación en cascada desde una sola sesión
|
|
627
|
+
|
|
628
|
+
El flujo completo puede ejecutarse colaborativamente en una sola conversación con un agente, donde el YAML actúa como checkpoint de revisión humana entre cada paso — no es generación ciega de código, sino colaboración estructurada con puntos de control explícitos:
|
|
629
|
+
|
|
630
|
+
```
|
|
631
|
+
1. Humano describe el negocio en lenguaje natural
|
|
632
|
+
2. Agente propone system.yaml
|
|
633
|
+
3. Humano refina system.yaml → eva system validate
|
|
634
|
+
4. Agente genera domain.yaml de cada módulo (entities + events + ports)
|
|
635
|
+
5. Humano revisa y ajusta
|
|
636
|
+
6. eva g entities por módulo → código completo generado
|
|
637
|
+
7. Sistema funcionando
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## Cómo Construir el system.yaml con un Agente de IA
|
|
643
|
+
|
|
644
|
+
### Qué debe saber el agente antes de empezar
|
|
645
|
+
|
|
646
|
+
Para que el agente proponga un `system.yaml` correcto y coherente, necesita recibir en el prompt inicial:
|
|
647
|
+
|
|
648
|
+
| Información | Por qué es necesaria |
|
|
649
|
+
|---|---|
|
|
650
|
+
| Descripción del negocio en lenguaje natural | Para inferir bounded contexts y responsabilidades de cada módulo |
|
|
651
|
+
| Lista de módulos identificados (opcional) | Para no inventar módulos — si ya hay claridad, evita iteraciones innecesarias |
|
|
652
|
+
| Flujos de negocio principales | Para determinar qué comunicación es async (Kafka) y qué es sync (HTTP) |
|
|
653
|
+
| `SYSTEM_YAML_GUIDE.md` adjunto | Para respetar la sintaxis exacta y las reglas de validación |
|
|
654
|
+
| Metadata del proyecto (groupId, Java version) | Para completar la sección `system:` correctamente |
|
|
655
|
+
|
|
656
|
+
### Prompt inicial recomendado
|
|
657
|
+
|
|
658
|
+
```
|
|
659
|
+
Eres un arquitecto de software experto en DDD y arquitectura hexagonal.
|
|
660
|
+
Tu tarea es construir el system.yaml para un proyecto eva4j.
|
|
661
|
+
|
|
662
|
+
Sistema: [descripción del negocio en 2-5 oraciones]
|
|
663
|
+
|
|
664
|
+
Módulos identificados:
|
|
665
|
+
- [módulo 1]: [responsabilidad]
|
|
666
|
+
- [módulo 2]: [responsabilidad]
|
|
667
|
+
- ...
|
|
668
|
+
|
|
669
|
+
Flujos principales:
|
|
670
|
+
- [flujo 1]: cuando [evento de negocio], [módulo A] notifica a [módulo B] y [módulo C]
|
|
671
|
+
- [flujo 2]: para [operación], [módulo X] necesita datos de [módulo Y]
|
|
672
|
+
|
|
673
|
+
Metadata:
|
|
674
|
+
- groupId: com.acme
|
|
675
|
+
- javaVersion: 21
|
|
676
|
+
- springBootVersion: 3.4.1
|
|
677
|
+
- database: postgresql
|
|
678
|
+
|
|
679
|
+
Adjunto: SYSTEM_YAML_GUIDE.md con la sintaxis completa y restricciones.
|
|
680
|
+
|
|
681
|
+
Genera el system.yaml completo respetando:
|
|
682
|
+
1. Sección system: con la metadata del proyecto
|
|
683
|
+
2. Sección modules: con los endpoints REST que expone cada módulo
|
|
684
|
+
3. Sección integrations.async: eventos Kafka con producer y consumers
|
|
685
|
+
4. Sección integrations.sync: llamadas HTTP entre módulos
|
|
686
|
+
5. Eventos nombrados en pasado (OrderPlacedEvent, no PlaceOrder)
|
|
687
|
+
6. Sin dependencias circulares síncronas
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
### Ciclo de refinamiento
|
|
691
|
+
|
|
692
|
+
```
|
|
693
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
694
|
+
│ 1. CONTEXTO │
|
|
695
|
+
│ Humano provee: descripción + módulos + flujos │
|
|
696
|
+
│ + SYSTEM_YAML_GUIDE.md adjunto │
|
|
697
|
+
└──────────────────────────────┬───────────────────────────────────┘
|
|
698
|
+
│
|
|
699
|
+
▼
|
|
700
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
701
|
+
│ 2. PROPUESTA │
|
|
702
|
+
│ Agente genera system.yaml v1 │
|
|
703
|
+
│ - Infiere módulos y responsabilidades │
|
|
704
|
+
│ - Define endpoints REST por módulo │
|
|
705
|
+
│ - Deduce integración async (Kafka) vs sync (HTTP) │
|
|
706
|
+
│ - Nombra eventos en pasado, modules en kebab-case │
|
|
707
|
+
└──────────────────────────────┬───────────────────────────────────┘
|
|
708
|
+
│
|
|
709
|
+
▼
|
|
710
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
711
|
+
│ 3. VALIDACIÓN AUTOMÁTICA │
|
|
712
|
+
│ eva system validate │
|
|
713
|
+
│ - Módulos referenciados inexistentes │
|
|
714
|
+
│ - Dependencias circulares síncronas │
|
|
715
|
+
│ - Eventos sin consumidores │
|
|
716
|
+
│ - Endpoints llamados no declarados en exposes: │
|
|
717
|
+
└──────────────────────────────┬───────────────────────────────────┘
|
|
718
|
+
│
|
|
719
|
+
▼
|
|
720
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
721
|
+
│ 4. REVISIÓN HUMANA │
|
|
722
|
+
│ Feedback específico al agente: │
|
|
723
|
+
│ - "El módulo X debería también exponer PUT /x/{id}/cancel" │
|
|
724
|
+
│ - "El evento Y debería consumirlo también el módulo Z" │
|
|
725
|
+
│ - "La llamada de A a B debería ser async, no sync" │
|
|
726
|
+
└──────────────────────────────┬───────────────────────────────────┘
|
|
727
|
+
│
|
|
728
|
+
▼
|
|
729
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
730
|
+
│ 5. REFINAMIENTO │
|
|
731
|
+
│ Agente aplica cambios mínimos → system.yaml v2 │
|
|
732
|
+
│ Iterar pasos 3-5 hasta aprobación │
|
|
733
|
+
└──────────────────────────────┬───────────────────────────────────┘
|
|
734
|
+
│
|
|
735
|
+
▼
|
|
736
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
737
|
+
│ 6. BOOTSTRAP │
|
|
738
|
+
│ eva generate system → proyecto + módulos + domain.yaml │
|
|
739
|
+
│ (con endpoints: pre-generados) │
|
|
740
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### Preguntas clave para guiar al agente
|
|
744
|
+
|
|
745
|
+
Cuando el agente propone el `system.yaml`, estas preguntas ayudan a refinar el diseño:
|
|
746
|
+
|
|
747
|
+
| Pregunta | Intención |
|
|
748
|
+
|---|---|
|
|
749
|
+
| "¿Qué módulo es responsable de X?" | Clarifica bounded contexts y evita duplicación de responsabilidad |
|
|
750
|
+
| "¿Este flujo debería ser async o sync?" | Define el estilo de integración según tolerancia a latencia y acoplamiento |
|
|
751
|
+
| "¿Quién es el productor natural de este evento?" | Define el ownership del evento en el dominio |
|
|
752
|
+
| "¿Qué endpoints necesita el frontend?" | Completa la sección `exposes:` de cada módulo |
|
|
753
|
+
| "¿Hay eventos que nadie consume todavía?" | Detecta gaps en el diseño antes de generar código |
|
|
754
|
+
| "¿Alguna llamada sync puede volverse async?" | Reduce acoplamiento temporal entre módulos |
|
|
755
|
+
|
|
756
|
+
### Criterios de calidad del system.yaml generado
|
|
757
|
+
|
|
758
|
+
Antes de aprobar el `system.yaml` propuesto por el agente, verificar:
|
|
759
|
+
|
|
760
|
+
- ✅ Cada módulo tiene **una sola responsabilidad** claramente identificable
|
|
761
|
+
- ✅ Los eventos están nombrados en **tiempo pasado** (`OrderPlacedEvent`, no `PlaceOrderEvent`)
|
|
762
|
+
- ✅ Los nombres de módulos usan **kebab-case** (`order-management`, no `OrderManagement`)
|
|
763
|
+
- ✅ **Sin dependencias circulares síncronas** (A llama a B y B llama a A)
|
|
764
|
+
- ✅ Módulos de solo lectura (reporting, notificaciones) son **consumidores**, nunca callers síncronos
|
|
765
|
+
- ✅ Los eventos contienen **datos mínimos necesarios** — el consumidor pide más si necesita
|
|
766
|
+
- ✅ Los endpoints en `exposes:` cubren **todas las operaciones** del módulo, no solo las que otros módulos usan
|
|
767
|
+
- ✅ `eva system validate` **no reporta errores** (solo advertencias aceptables)
|
|
768
|
+
|
|
769
|
+
### Por qué el agente necesita el SYSTEM_YAML_GUIDE.md
|
|
770
|
+
|
|
771
|
+
Sin una referencia técnica precisa, el agente puede generar YAML semánticamente válido pero estructuralmente incorrecto para eva4j — por ejemplo: usar `consumes:` en `system.yaml` (que no existe en ese nivel), invertir la dirección de `sync.caller/calls`, o nombrar los eventos sin sufijo `Event`. El `SYSTEM_YAML_GUIDE.md` actúa como **gramática contractual** que el agente debe respetar, equivalente al rol que AGENTS.md cumple para la generación de código Java.
|
|
772
|
+
|
|
773
|
+
---
|
|
774
|
+
|
|
775
|
+
## Preguntas Abiertas
|
|
776
|
+
|
|
777
|
+
1. ~~**¿`system.yaml` como fuente de verdad permanente o solo bootstrap inicial?**~~
|
|
778
|
+
~~¿Los `domain.yaml` son siempre derivados del `system.yaml` (un cambio en uno requiere actualizar el otro), o el `system.yaml` solo se usa para el arranque inicial y después cada módulo evoluciona libremente?~~
|
|
779
|
+
**Resuelto:** el `system.yaml` es un artefacto **iterativo** que evoluciona con el proyecto. Es normal que emerjan nuevos módulos, endpoints o integraciones no previstos inicialmente. Cada cambio sigue el mismo ciclo: editar → `eva system validate` → actualizar `domain.yaml` afectado → `eva g entities`. `eva generate system` es re-ejecutable: agrega módulos nuevos que aún no existen y **siempre sobreescribe el `domain.yaml`** de todos los módulos con el esqueleto actualizado desde `system.yaml` (los módulos ya existentes no se recrean, solo se regenera su `domain.yaml`).
|
|
780
|
+
|
|
781
|
+
2. ~~**¿Cómo manejar eventos cuyos campos no están definidos en `system.yaml`?**~~
|
|
782
|
+
~~El `system.yaml` no conoce los campos de los eventos (ese es territorio del dominio). ¿El `integration.yaml → consumes[].fields` es suficiente como contrato, o hace falta un schema compartido?~~
|
|
783
|
+
**Resuelto:** los campos del evento se declaran en `domain.yaml → events[].fields` del módulo productor. El consumidor los conoce al leer el `domain.yaml` del productor. No hace falta un schema compartido separado.
|
|
784
|
+
|
|
785
|
+
3. **¿Soporte para múltiples entornos en `system.yaml`?**
|
|
786
|
+
Los `baseUrl` de los `calls:` son distintos por entorno. ¿Se resuelve con variables de entorno, o el `system.yaml` puede tener secciones por entorno?
|
|
787
|
+
|
|
788
|
+
4. **¿Qué pasa con módulos que no están en `system.yaml` pero ya existen?**
|
|
789
|
+
Para proyectos existentes, ¿hay un comando `eva system scan` que genere el `system.yaml` a partir de la estructura actual?
|
|
790
|
+
|
|
791
|
+
5. ~~**Granularidad de `exposes:`**~~
|
|
792
|
+
~~¿Los endpoints en `exposes:` solo son documentales, o en el futuro podrían incluir request/response bodies para generar también los DTOs del controller?~~
|
|
793
|
+
**Resuelto:** sintaxis objeto obligatoria con `method`, `path`, `useCase` y `description`. El campo `useCase` permite a `eva generate system` pre-generar la sección `endpoints:` completa en `domain.yaml`; el desarrollador solo rellena `aggregates:`.
|
|
794
|
+
|
|
795
|
+
---
|
|
796
|
+
|
|
797
|
+
*Este documento es un artefacto de diseño vivo. Las ideas aquí plasmadas están sujetas a revisión y refinamiento.*
|