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
@@ -5,7 +5,7 @@ const path = require('path');
5
5
  const fs = require('fs-extra');
6
6
  const ConfigManager = require('../utils/config-manager');
7
7
  const { isEva4jProject, moduleExists } = require('../utils/validator');
8
- const { toPackagePath, toPascalCase } = require('../utils/naming');
8
+ const { toPackagePath, toPascalCase, toCamelCase } = require('../utils/naming');
9
9
  const { renderAndWrite } = require('../utils/template-engine');
10
10
  const ChecksumManager = require('../utils/checksum-manager');
11
11
 
@@ -32,6 +32,9 @@ async function generateUsecaseCommand(moduleName, usecaseName, options = {}) {
32
32
  const { packageName } = projectConfig;
33
33
  const packagePath = toPackagePath(packageName);
34
34
 
35
+ // Normalise module name to camelCase (system.yaml uses kebab-case, .eva4j.json stores camelCase)
36
+ moduleName = toCamelCase(moduleName);
37
+
35
38
  // Validate module exists
36
39
  if (!(await configManager.moduleExists(moduleName))) {
37
40
  console.error(chalk.red(`❌ Module '${moduleName}' not found in project configuration`));
@@ -47,6 +47,9 @@ Antes de generar nada, lee estos archivos del proyecto para obtener contexto:
47
47
  | 6.5 | `system/c4-context.mmd` + `system/c4-container.mmd` | Este archivo (sección C4) |
48
48
  | 7 | `system/{module}.yaml` (uno por módulo) | `references/domain-yaml-spec.md` |
49
49
  | 8 | `system/{module}.md` (uno por módulo) | `references/module-spec.md` |
50
+ | 9a | `AGENTS.md` (project root — rewrite) | Este archivo (Paso 9) |
51
+ | 9b | `system/VALIDATION_FLOWS.md` | Este archivo (Paso 9) |
52
+ | 9c | `system/USER_FLOWS.md` | Este archivo (Paso 9) |
50
53
 
51
54
  Ejecuta **todos** los pasos en orden antes de devolver el control al usuario.
52
55
 
@@ -54,7 +57,14 @@ Ejecuta **todos** los pasos en orden antes de devolver el control al usuario.
54
57
 
55
58
  ## Paso 1 — Recopilar información
56
59
 
57
- Si el usuario no proveyó todos los datos, **pregunta** antes de generar:
60
+ **Antes de preguntar nada**, verifica si ya existen artefactos del skill `requirements-elicitation`:
61
+ - `system/FUNCTIONAL_REQUIREMENTS.md` — casos de uso, actores, ciclos de vida, integraciones externas
62
+ - `system/PRODUCT_FLOWS.md` — flujos de negocio por actor (happy paths + alternativos)
63
+ - `system/BUSINESS_RULES.md` — reglas de negocio, invariantes, restricciones
64
+
65
+ Si existen, **léelos primero** y extrae de ellos el contexto de negocio. Reduce o elimina las preguntas que ya están respondidas por estos archivos. Estos documentos son la fuente de verdad funcional — los módulos, casos de uso, estados y reglas que diseñes deben ser consistentes con lo que describen.
66
+
67
+ Si el usuario no proveyó todos los datos y los archivos anteriores no existen, **pregunta** antes de generar:
58
68
 
59
69
  0. **Contexto del negocio** — ¿Cuál es el dominio? Actores, procesos clave, reglas importantes.
60
70
  1. **¿Usa mensajería asíncrona?** `kafka` | `rabbitmq` | `sns-sqs`
@@ -62,11 +72,32 @@ Si el usuario no proveyó todos los datos, **pregunta** antes de generar:
62
72
  3. **Endpoints REST** por módulo (método + path + caso de uso)
63
73
  4. **Flujos async**: evento → productor → consumidores + `useCase` de cada consumidor
64
74
  5. **Llamadas sync**: caller → destino → endpoints usados
75
+ 6. **Dependencias de datos cross-module**: ¿algún módulo necesita datos de otro para validar o enriquecer? → candidato a **Read Model** (proyección local mantenida por eventos) en vez de llamada sync
65
76
 
66
77
  > Si `system/system.yaml` ya existe, léelo y pregunta solo por cambios.
67
78
 
68
79
  Aplica el rol funcional: sugiere módulos necesarios no mencionados, propone flujos async coherentes, anticipa invariantes. Confirma antes de agregar elementos no solicitados.
69
80
 
81
+ ### Read Models — decisión de diseño
82
+
83
+ Cuando un módulo necesita datos de otro módulo, evalúa antes de decidir entre `ports:` (sync HTTP) y `readModels:` (async, proyección local):
84
+
85
+ | Pregunta | Si la respuesta es SÍ → |
86
+ |---|---|
87
+ | ¿El dato se consulta en cada request o en operaciones frecuentes? | `readModels:` |
88
+ | ¿Se tolera consistencia eventual (ms de delay)? | `readModels:` |
89
+ | ¿Se prepara el sistema para microservicios (`eva detach`)? | `readModels:` |
90
+ | ¿Se necesita consistencia fuerte (ej: saldo financiero)? | `ports:` |
91
+ | ¿Es una llamada infrecuente y simple (ej: lookup puntual)? | `ports:` |
92
+
93
+ **Patrón típico:** Si un módulo ya consume eventos de otro módulo (`listeners:`) y además llama sync para obtener datos del mismo módulo (`ports:`), es candidato fuerte a reemplazar el port por un readModel.
94
+
95
+ Si decides usar readModel:
96
+ 1. En `system.yaml`: declarar eventos con `consumers[].readModel:` en vez de `useCase:`
97
+ 2. En `{module}.yaml`: declarar `readModels:` con `source`, `tableName`, `fields`, `syncedBy`
98
+ 3. Eliminar la entrada `integrations.sync[]` que reemplaza
99
+ 4. Asegurar que el módulo fuente emita los eventos necesarios en sus `events:` — usar `lifecycle:` para eventos CRUD (`create`/`update`/`delete`/`softDelete`) en vez de `triggers:`
100
+
70
101
  ---
71
102
 
72
103
  ## Paso 2 — Estructura del system.yaml
@@ -108,6 +139,13 @@ integrations:
108
139
  consumers:
109
140
  - module: payments
110
141
  useCase: HandleOrderPlaced
142
+ # Read Model sync — usa readModel: en vez de useCase:
143
+ - event: ProductCreatedEvent
144
+ producer: products
145
+ topic: PRODUCT_CREATED
146
+ consumers:
147
+ - module: orders
148
+ readModel: ProductReadModel # proyección local de datos cross-module
111
149
  sync:
112
150
  - caller: orders
113
151
  calls: customers
@@ -134,6 +172,8 @@ integrations:
134
172
  - Sin port names genéricos compartidos entre módulos
135
173
  - `consumers[].useCase` siempre presente y en PascalCase
136
174
  - `calls.using:` solo referencia endpoints de `exposes:` del destino
175
+ - Cada consumer declara exactamente `useCase:` (lógica de negocio) o `readModel:` (proyección local), nunca ambos
176
+ - `readModel:` PascalCase + sufijo `ReadModel` — su módulo consumidor declara `readModels:` en domain.yaml
137
177
 
138
178
  ---
139
179
 
@@ -146,6 +186,8 @@ Antes de proponer el `system.yaml`, verifica:
146
186
  - [ ] Sin dependencias circulares síncronas
147
187
  - [ ] Todos los `consumers[].module` existen en `modules:`
148
188
  - [ ] Todos los `consumers[].useCase` presentes y en PascalCase
189
+ - [ ] `consumers[]` con `readModel:` en PascalCase + sufijo `ReadModel`
190
+ - [ ] Cada consumer tiene exactamente `useCase:` o `readModel:`, nunca ambos
149
191
  - [ ] Todos los `calls.using:` existen en `exposes:` del destino
150
192
  - [ ] Módulos pasivos no son `caller`
151
193
  - [ ] Todo en inglés
@@ -257,6 +299,7 @@ C4Container
257
299
  - `ContainerQueue()` para el broker — solo si `messaging.enabled: true`; usar el tipo de `messaging.broker`
258
300
  - `System_Ext()` fuera del boundary para servicios externos
259
301
  - **Flechas async siempre pasan por el broker**: `producer → broker` y `broker → consumer`, nunca directo
302
+ - **Read Model sync también pasa por el broker**: `Rel(source, broker, "ProductCreatedEvent", "Kafka")` + `Rel(broker, consumer, "Sync ProductReadModel", "Kafka")` — no usar flecha directa
260
303
  - **Flechas sync** directas entre containers: de caller a callee con label del port name
261
304
  - Los `Rel()` de eventos incluyen el nombre del evento en el campo `technology`/label
262
305
  - Derivar actores `Person()` de quienes consumen los `exposes[]`
@@ -302,7 +345,118 @@ Antes de guardar, verifica:
302
345
 
303
346
  Lee `references/domain-yaml-spec.md` para la especificación completa de estructura, reglas, restricciones y checklist del `system/{module}.yaml`.
304
347
 
305
- Para cada módulo en `modules:`, genera `system/{nombre-del-modulo}.yaml` con: aggregates, entities, valueObjects, enums (con transitions si aplica), events, endpoints, listeners y ports — todo inferido del `system.yaml`.
348
+ Para cada módulo en `modules:`, genera `system/{nombre-del-modulo}.yaml` con: aggregates, entities, valueObjects, enums (con transitions si aplica), events, endpoints, listeners, ports y **readModels** — todo inferido del `system.yaml`.
349
+
350
+ ### Endpoints en módulos con múltiples agregados
351
+
352
+ Si el módulo tiene **2 o más agregados** (ej: `Product` + `Category`), la sección `endpoints:` debe usar `basePath: ""` (string vacío) y paths **absolutos** en cada operación:
353
+
354
+ ```yaml
355
+ # Módulo con 2+ agregados → basePath vacío
356
+ endpoints:
357
+ basePath: ""
358
+ versions:
359
+ - version: v1
360
+ operations:
361
+ - useCase: CreateProduct
362
+ method: POST
363
+ path: /products
364
+ - useCase: CreateCategory
365
+ method: POST
366
+ path: /categories
367
+ ```
368
+
369
+ Si el módulo tiene **un solo agregado**, usar `basePath: /recurso` con paths relativos (ej: `/`, `/{id}`).
370
+
371
+ **NUNCA usar `basePath: /`** (con slash) — genera trailing slash en `@RequestMapping`. Usar `basePath: ""` (vacío).
372
+
373
+ ### Inferencia de readModels desde system.yaml
374
+
375
+ Cuando `integrations.async[].consumers[]` tiene `readModel:` y el `module` es el módulo actual:
376
+ 1. Agrupar todos los eventos del mismo `readModel:` → una entrada `readModels:` con múltiples `syncedBy`
377
+ 2. `source.module` = el `producer` de esas integraciones async
378
+ 3. `source.aggregate` = derivar del nombre del readModel (ej: `ProductReadModel` → `Product`)
379
+ 4. `tableName` = `rm_` + consumer module + `_` + source module en snake_case (ej: `rm_orders_products`)
380
+ 5. `fields` = inferir del payload del evento fuente (incluir siempre `id`)
381
+ 6. `syncedBy[].action` = `UPSERT` para Created/Updated, `SOFT_DELETE` para Deactivated, `DELETE` para Deleted
382
+ 7. Si había una entrada `integrations.sync[]` al mismo módulo fuente → **no generar `ports:`** para esa llamada (el readModel la reemplaza)
383
+
384
+ ### Lifecycle events en módulos fuente
385
+
386
+ Cuando el módulo actual es `producer` en `integrations.async[]` y algún consumer tiene `readModel:`, los eventos de este módulo deben usar `lifecycle:` (operación CRUD) en vez de `triggers:` (transición de estado).
387
+
388
+ Para cada evento de este módulo consumido por un readModel:
389
+
390
+ 1. Agregar `lifecycle:` al evento — derivar el valor del nombre del evento:
391
+ | Patrón del nombre | `lifecycle:` |
392
+ |---|---|
393
+ | `*CreatedEvent`, `*RegisteredEvent` | `create` |
394
+ | `*UpdatedEvent` | `update` |
395
+ | `*DeletedEvent` | `delete` |
396
+ | `*DeactivatedEvent` | `softDelete` |
397
+
398
+ 2. **NO** agregar `triggers:` — estos eventos son CRUD, no transiciones de estado
399
+ 3. Si `lifecycle: softDelete` → la entidad raíz **debe** tener `hasSoftDelete: true`
400
+ 4. Si `lifecycle: delete` → la entidad raíz **NO debe** tener `hasSoftDelete: true`
401
+ 5. `fields:` del evento debe incluir **todos** los campos declarados en el `readModels[].fields` del módulo consumidor (el payload es la fuente de verdad de la proyección)
402
+ 6. Siempre incluir `{entityName}Id` como campo (se mapea a `aggregateId` del DomainEvent base)
403
+ 7. `fields:` del lifecycle event solo puede contener: (a) `{entityName}Id` (aggregateId), (b) campos que existen en la entidad raíz, (c) campos temporales `*At` + `LocalDateTime`. No incluir campos que no existan en la entidad — genera error `C2-010`
404
+ 8. Los campos de los readModels consumidores deben ser subconjunto de los campos de la entidad raíz del productor. Si el readModel necesita un campo, ese campo debe existir en la entidad fuente — de lo contrario los lifecycle events no podrán emitirlo (C2-010) y el campo siempre será null (C1-007)
405
+
406
+ **Ejemplo — módulo `products` como fuente de `ProductReadModel`:**
407
+
408
+ ```yaml
409
+ aggregates:
410
+ - name: Product
411
+ entities:
412
+ - name: product
413
+ isRoot: true
414
+ tableName: products
415
+ hasSoftDelete: true
416
+ audit:
417
+ enabled: true
418
+ fields:
419
+ - name: id
420
+ type: String
421
+ - name: name
422
+ type: String
423
+ - name: price
424
+ type: BigDecimal
425
+ - name: status
426
+ type: String
427
+ readOnly: true
428
+ defaultValue: "ACTIVE"
429
+ events:
430
+ - name: ProductCreatedEvent
431
+ lifecycle: create
432
+ fields:
433
+ - name: productId
434
+ type: String
435
+ - name: name
436
+ type: String
437
+ - name: price
438
+ type: BigDecimal
439
+ - name: status
440
+ type: String
441
+ - name: ProductUpdatedEvent
442
+ lifecycle: update
443
+ fields:
444
+ - name: productId
445
+ type: String
446
+ - name: name
447
+ type: String
448
+ - name: price
449
+ type: BigDecimal
450
+ - name: status
451
+ type: String
452
+ - name: ProductDeactivatedEvent
453
+ lifecycle: softDelete
454
+ fields:
455
+ - name: productId
456
+ type: String
457
+ - name: deactivatedAt
458
+ type: LocalDateTime
459
+ ```
306
460
 
307
461
  ---
308
462
 
@@ -314,10 +468,197 @@ Para cada módulo, genera `system/{nombre-del-modulo}.md` con: rol del módulo,
314
468
 
315
469
  ---
316
470
 
471
+ ## Paso 9 — Artefactos post-diseño
472
+
473
+ Inmediatamente después de completar el Paso 8, genera tres artefactos finales que contextualizan el sistema recién diseñado.
474
+
475
+ ### Paso 9a — Reescribir AGENTS.md (project-specific)
476
+
477
+ Reescribe el archivo `AGENTS.md` en la **raíz del proyecto** con contenido específico para el sistema diseñado.
478
+
479
+ **Proceso:**
480
+
481
+ 1. Lee el `AGENTS.md` actual como template base
482
+ 2. Analiza `system/system.yaml` y todos los `system/{module}.yaml` para detectar qué features se usan:
483
+ - Tipo de broker (Kafka / RabbitMQ / ninguno)
484
+ - `readModels:` en algún módulo
485
+ - `ports:` (llamadas HTTP síncronas)
486
+ - `listeners:` (consumidores de eventos)
487
+ - `events:` con `triggers:` vs `lifecycle:`
488
+ - `hasSoftDelete` en alguna entidad
489
+ - `audit.trackUser` en alguna entidad
490
+ - Value Objects con `methods:`
491
+ - Enums con `transitions:` e `initialValue`
492
+ - Flags de campo: `readOnly`, `hidden`, `defaultValue`, `validations`, `reference`
493
+ 3. **Poda** secciones de features no usados según estas reglas:
494
+
495
+ | Condición | Sección a eliminar |
496
+ |---|---|
497
+ | Sin Temporal | "Temporal Workflows" completa |
498
+ | Sin `readModels:` | Subsecciones readModels de "Características Avanzadas" y checklist |
499
+ | Sin `ports:` | Subsecciones ports |
500
+ | Sin `listeners:` | Subsecciones listeners |
501
+ | Sin `hasSoftDelete` | Sección soft delete y checklist items |
502
+ | Sin `audit.trackUser` | Infraestructura UserContextFilter/UserContextHolder/AuditorAwareImpl (mantener audit básico si `audit.enabled`) |
503
+ | Sin VO `methods:` | "Value Objects con Métodos" |
504
+ | Sin enum `transitions:` | "Enums con Ciclo de Vida" |
505
+
506
+ 4. **Especializa** el contenido restante:
507
+ - Reemplaza ejemplos genéricos (`User`, `Order`) con entidades/módulos reales del proyecto
508
+ - Actualiza la sección de comandos `eva` con los nombres de módulos reales
509
+ - Actualiza el ejemplo de `domain.yaml` con la estructura real del proyecto
510
+ - Reduce el checklist a solo items relevantes para este proyecto
511
+ 5. Agrega un **header de contexto** al inicio:
512
+
513
+ ```markdown
514
+ # AI Agent Guide — {System Name}
515
+
516
+ ## Project Overview
517
+ - **System:** {name} — {brief description from system.yaml}
518
+ - **Modules:** {list of modules with 1-line descriptions}
519
+ - **Messaging:** {broker type or "none"}
520
+ - **Database:** {database type}
521
+ - **Java:** {javaVersion} / **Spring Boot:** {springBootVersion}
522
+ ```
523
+
524
+ 6. **Siempre conserva** (son universales): principios DDD, arquitectura hexagonal, reglas de mappers, reglas de DTOs, diagramas de flujo de datos (Command write / Query read), patrones de testing
525
+ 7. Escribe **todo en inglés**
526
+ 8. **Límite: ≤ 1000 líneas** — poda agresivamente, comprime ejemplos, evita redundancia
527
+
528
+ ---
529
+
530
+ ### Paso 9b — Crear system/VALIDATION_FLOWS.md
531
+
532
+ Genera `system/VALIDATION_FLOWS.md` con los flujos de validación técnica del sistema. Toda la información se deriva de `system.yaml` y los `{module}.yaml`.
533
+
534
+ **Estructura obligatoria:**
535
+
536
+ ```markdown
537
+ # Validation Flows — {System Name}
538
+
539
+ ## Prerequisites
540
+ - Services: {infrastructure requerida — DB, broker, etc.}
541
+ - Startup order: {si relevante}
542
+ - Base URLs: {por módulo si difieren}
543
+
544
+ ## 1. Module Validation
545
+
546
+ ### 1.1 {Module Name}
547
+
548
+ #### CRUD Operations
549
+ | # | Operation | Endpoint | Payload/Params | Expected Result | Validates |
550
+ |---|-----------|----------|----------------|-----------------|------ ----|
551
+ | 1 | Create | POST /x | {key fields} | 201 + entity | {invariant} |
552
+ | 2 | Get by ID | GET /x/{id} | — | 200 + entity | — |
553
+ | 3 | List | GET /x | — | 200 + page | — |
554
+ | 4 | Update | PUT /x/{id} | {fields} | 200 + updated | — |
555
+ | 5 | Delete | DELETE /x/{id} | — | 204 | — |
556
+
557
+ #### State Transitions (if module has enum transitions)
558
+ | # | Transition | Endpoint | Precondition | Expected | Event Emitted |
559
+ |---|-----------|----------|--------------|----------|---------------|
560
+ | 1 | DRAFT→PUBLISHED | PUT /x/{id}/publish | exists in DRAFT | 200, status=PUBLISHED | XPublishedEvent |
561
+
562
+ #### Business Rules
563
+ | # | Rule | How to Trigger | Expected Error |
564
+ |---|------|----------------|----------------|
565
+
566
+ (repeat per module)
567
+
568
+ ## 2. Integration Flows
569
+
570
+ ### 2.1 {Flow Name}: {Event} → {Consumer Module}
571
+ **Trigger:** {action that emits the event}
572
+ **Steps:**
573
+ 1. {Create/modify entity in producer module}
574
+ 2. {Event emitted}: {EventName} on topic {TOPIC_NAME}
575
+ 3. {Consumer module} processes via {useCase}
576
+ 4. **Verify:** {expected state change in consumer}
577
+
578
+ (repeat per async integration)
579
+
580
+ ## 3. Read Model Synchronization (if applicable)
581
+ ### 3.1 {ReadModelName}
582
+ | Source Event | Action | Verification |
583
+ |---|---|---|
584
+ | XCreatedEvent | UPSERT | Query rm_table, record exists |
585
+ | XUpdatedEvent | UPSERT | Fields updated |
586
+ | XDeactivatedEvent | SOFT_DELETE | Record marked deleted |
587
+
588
+ ## 4. Sync Port Calls (if applicable)
589
+ ### 4.1 {PortName}: {caller} → {target}
590
+ | Method | Expected | Fallback |
591
+ |---|---|---|
592
+
593
+ ## 5. Error & Edge Cases
594
+ | # | Scenario | Steps | Expected Error |
595
+ |---|----------|-------|----------------|
596
+ | 1 | Create with missing required field | POST /x without {field} | 400 + validation message |
597
+ | 2 | Invalid state transition | PUT /x/{id}/action when invalid state | 400/409 + business error |
598
+ | 3 | Get non-existent entity | GET /x/{invalid-id} | 404 |
599
+ ```
600
+
601
+ **Reglas:**
602
+ - Cada flujo debe ser concreto: paths reales, nombres de eventos reales, campos reales del proyecto
603
+ - Incluir payloads JSON sugeridos donde sea útil
604
+ - Omitir secciones enteras si no aplican (ej: sin readModels → omitir sección 3)
605
+ - Todo en inglés
606
+
607
+ ---
608
+
609
+ ### Paso 9c — Crear system/USER_FLOWS.md
610
+
611
+ Genera `system/USER_FLOWS.md` con los flujos end-to-end desde la perspectiva del usuario.
612
+
613
+ **Estructura obligatoria:**
614
+
615
+ ```markdown
616
+ # User Flows — {System Name}
617
+
618
+ ## Actors
619
+ | Actor | Description | Modules Interacted |
620
+ |-------|-------------|--------------------|
621
+ | {Actor 1} | {role description} | {module list} |
622
+
623
+ ## Flow 1: {Business Process Name}
624
+ **Actor:** {who}
625
+ **Goal:** {what they want to achieve}
626
+ **Preconditions:** {initial state}
627
+
628
+ ### Happy Path
629
+ | Step | User Action | System Response | Behind the Scenes |
630
+ |------|-------------|-----------------|-------------------|
631
+ | 1 | {does X} | {sees Y} | {endpoint called, event emitted, etc.} |
632
+ | 2 | {does Z} | {sees W} | {consumer processes, state changes} |
633
+
634
+ ### Alternative Paths
635
+ | Condition | At Step | What Happens |
636
+ |-----------|---------|---------- ---|
637
+ | {condition} | {N} | {alternative outcome} |
638
+
639
+ ### Error Paths
640
+ | Error | At Step | User Sees |
641
+ |-------|---------|----------|
642
+ | {error} | {N} | {error message/behavior} |
643
+
644
+ (repeat per major business flow)
645
+ ```
646
+
647
+ **Reglas:**
648
+ - Derivar actores de quienes consumen los `exposes[]` (misma fuente que `Person()` del C4 Context)
649
+ - Cada flujo es un **escenario de negocio completo** que cruza módulos cuando aplica
650
+ - La columna "Behind the Scenes" conecta la experiencia de usuario con la realidad técnica (eventos, procesamiento async, state changes)
651
+ - Incluir al menos un flujo por cada camino principal de caso de uso del sistema
652
+ - Foco en comportamiento observable por el usuario, no implementación interna
653
+ - Todo en inglés
654
+
655
+ ---
656
+
317
657
  ## Ciclo de refinamiento
318
658
 
319
659
  Después de entregar v1, si el usuario pide ajustes:
320
660
  - Aplica el **cambio mínimo** necesario
321
661
  - Revalida el checklist del Paso 4
322
662
  - Actualiza `system.md`, `c4-context.mmd`, `c4-container.mmd`, `{module}.yaml` y `{module}.md` afectados
663
+ - Actualiza `AGENTS.md`, `VALIDATION_FLOWS.md` y `USER_FLOWS.md` si fueron afectados por el cambio
323
664
  - Entrega solo el diff explicado