eva4j 1.0.16 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/AGENTS.md +220 -5
  2. package/DOMAIN_YAML_GUIDE.md +188 -3
  3. package/FUTURE_FEATURES.md +33 -52
  4. package/QUICK_REFERENCE.md +8 -4
  5. package/bin/eva4j.js +70 -2
  6. package/config/defaults.json +1 -0
  7. package/docs/CAMUNDA_DMN_GUIDE.md +1380 -0
  8. package/docs/KAFKA_PRODUCTION_CONFIG.md +441 -0
  9. package/docs/RABBITMQ_PRODUCTION_CONFIG.md +227 -0
  10. package/docs/commands/ADD_RABBITMQ_CLIENT.md +192 -0
  11. package/docs/commands/EVALUATE_SYSTEM.md +290 -10
  12. package/docs/commands/GENERATE_RABBITMQ_EVENT.md +341 -0
  13. package/docs/commands/GENERATE_RABBITMQ_LISTENER.md +595 -0
  14. package/docs/commands/GENERATE_TEMPORAL_FLOW.md +52 -12
  15. package/docs/commands/INDEX.md +27 -3
  16. package/docs/prototype/TEMPORAL_COMMUNICATION_PATTERNS.md +731 -0
  17. package/docs/prototype/TEMPORAL_DESIGN_METHODOLOGY.md +740 -0
  18. package/docs/prototype/system/RISKS.md +277 -0
  19. package/docs/prototype/system/customers.yaml +133 -0
  20. package/docs/prototype/system/inventory.yaml +109 -0
  21. package/docs/prototype/system/notifications.yaml +131 -0
  22. package/docs/prototype/system/orders.yaml +241 -0
  23. package/docs/prototype/system/payments.yaml +256 -0
  24. package/docs/prototype/system/products.yaml +168 -0
  25. package/docs/prototype/system/system.yaml +269 -0
  26. package/examples/domain-endpoints-multi-aggregate.yaml +140 -0
  27. package/examples/domain-events.yaml +26 -0
  28. package/examples/domain-read-models.yaml +113 -0
  29. package/examples/system/customer.yaml +89 -0
  30. package/examples/system/orders.yaml +119 -0
  31. package/examples/system/product.yaml +27 -0
  32. package/examples/system/system.yaml +80 -0
  33. package/package.json +1 -1
  34. package/read-model-spec.md +664 -0
  35. package/src/agents/design-gap-analyst-temporal.agent.md +452 -0
  36. package/src/agents/design-gap-analyst.agent.md +383 -0
  37. package/src/agents/design-reviewer-temporal.agent.md +412 -0
  38. package/src/agents/design-reviewer.agent.md +34 -5
  39. package/src/agents/implement-use-cases.prompt.md +179 -0
  40. package/src/agents/ux-gap-analyst.agent.md +412 -0
  41. package/src/commands/add-rabbitmq-client.js +261 -0
  42. package/src/commands/add-temporal-client.js +22 -2
  43. package/src/commands/build.js +267 -11
  44. package/src/commands/evaluate-system.js +700 -13
  45. package/src/commands/generate-entities.js +560 -24
  46. package/src/commands/generate-http-exchange.js +3 -0
  47. package/src/commands/generate-kafka-event.js +3 -0
  48. package/src/commands/generate-kafka-listener.js +3 -0
  49. package/src/commands/generate-rabbitmq-event.js +665 -0
  50. package/src/commands/generate-rabbitmq-listener.js +205 -0
  51. package/src/commands/generate-record.js +2 -2
  52. package/src/commands/generate-resource.js +4 -1
  53. package/src/commands/generate-temporal-activity.js +970 -33
  54. package/src/commands/generate-temporal-flow.js +98 -38
  55. package/src/commands/generate-temporal-system.js +708 -0
  56. package/src/commands/generate-usecase.js +4 -1
  57. package/src/skills/build-system-yaml/SKILL.md +343 -2
  58. package/src/skills/build-system-yaml/references/domain-yaml-spec.md +253 -26
  59. package/src/skills/build-system-yaml/references/module-spec.md +90 -9
  60. package/src/skills/build-system-yaml/references/system-yaml-spec.md +36 -0
  61. package/src/skills/build-temporal-system/SKILL.md +752 -0
  62. package/src/skills/build-temporal-system/references/temporal-communication-patterns.md +167 -0
  63. package/src/skills/build-temporal-system/references/temporal-domain-yaml-spec.md +449 -0
  64. package/src/skills/build-temporal-system/references/temporal-module-spec.md +353 -0
  65. package/src/skills/build-temporal-system/references/temporal-system-yaml-spec.md +326 -0
  66. package/src/skills/implement-use-case/SKILL.md +350 -0
  67. package/src/skills/implement-use-case/references/use-case-patterns.md +980 -0
  68. package/src/skills/requirements-elicitation/SKILL.md +228 -0
  69. package/src/skills/requirements-elicitation/references/interview-framework.md +260 -0
  70. package/src/skills/requirements-elicitation/references/output-templates.md +368 -0
  71. package/src/utils/bounded-context-diagram.js +844 -0
  72. package/src/utils/config-manager.js +4 -2
  73. package/src/utils/domain-validator.js +495 -17
  74. package/src/utils/naming.js +20 -0
  75. package/src/utils/system-validator.js +169 -11
  76. package/src/utils/system-yaml-parser.js +318 -0
  77. package/src/utils/temporal-validator.js +497 -0
  78. package/src/utils/validator.js +3 -1
  79. package/src/utils/yaml-to-entity.js +281 -9
  80. package/templates/aggregate/AggregateRepository.java.ejs +4 -0
  81. package/templates/aggregate/AggregateRepositoryImpl.java.ejs +8 -0
  82. package/templates/aggregate/AggregateRoot.java.ejs +38 -4
  83. package/templates/aggregate/DomainEventHandler.java.ejs +116 -22
  84. package/templates/aggregate/JpaAggregateRoot.java.ejs +4 -4
  85. package/templates/aggregate/JpaEntity.java.ejs +2 -2
  86. package/templates/base/docker/rabbitmq-services.yaml.ejs +12 -0
  87. package/templates/base/resources/parameters/develop/kafka.yaml.ejs +5 -0
  88. package/templates/base/resources/parameters/develop/rabbitmq.yaml.ejs +15 -0
  89. package/templates/base/resources/parameters/develop/temporal.yaml.ejs +0 -3
  90. package/templates/base/resources/parameters/local/kafka.yaml.ejs +5 -0
  91. package/templates/base/resources/parameters/local/rabbitmq.yaml.ejs +15 -0
  92. package/templates/base/resources/parameters/local/temporal.yaml.ejs +0 -3
  93. package/templates/base/resources/parameters/production/kafka.yaml.ejs +39 -8
  94. package/templates/base/resources/parameters/production/rabbitmq.yaml.ejs +32 -0
  95. package/templates/base/resources/parameters/production/temporal.yaml.ejs +0 -3
  96. package/templates/base/resources/parameters/test/kafka.yaml.ejs +12 -6
  97. package/templates/base/resources/parameters/test/rabbitmq.yaml.ejs +15 -0
  98. package/templates/base/resources/parameters/test/temporal.yaml.ejs +0 -3
  99. package/templates/base/root/AGENTS.md.ejs +1 -1
  100. package/templates/crud/DeleteCommandHandler.java.ejs +19 -1
  101. package/templates/crud/EndpointsController.java.ejs +1 -1
  102. package/templates/crud/ScaffoldCommand.java.ejs +5 -2
  103. package/templates/crud/ScaffoldCommandHandler.java.ejs +3 -1
  104. package/templates/crud/ScaffoldQuery.java.ejs +5 -2
  105. package/templates/crud/ScaffoldQueryHandler.java.ejs +3 -1
  106. package/templates/crud/SubEntityRemoveCommand.java.ejs +1 -1
  107. package/templates/crud/UpdateCommandHandler.java.ejs +53 -2
  108. package/templates/evaluate/report.html.ejs +1447 -90
  109. package/templates/kafka-event/KafkaConfigBean.java.ejs +1 -1
  110. package/templates/kafka-event/KafkaMessageBroker.java.ejs +3 -3
  111. package/templates/ports/PortAclMapper.java.ejs +35 -0
  112. package/templates/ports/PortFeignAdapter.java.ejs +7 -22
  113. package/templates/ports/PortFeignClient.java.ejs +4 -0
  114. package/templates/ports/PortResponseDto.java.ejs +1 -1
  115. package/templates/rabbitmq-event/RabbitConfigBean.java.ejs +33 -0
  116. package/templates/rabbitmq-event/RabbitConfigExchange.java.ejs +12 -0
  117. package/templates/rabbitmq-event/RabbitMessageBroker.java.ejs +35 -0
  118. package/templates/rabbitmq-event/RabbitMessageBrokerMethod.java.ejs +9 -0
  119. package/templates/rabbitmq-listener/RabbitConfigConsumerBean.java.ejs +33 -0
  120. package/templates/rabbitmq-listener/RabbitConfigConsumerExchange.java.ejs +12 -0
  121. package/templates/rabbitmq-listener/RabbitListenerClass.java.ejs +82 -0
  122. package/templates/rabbitmq-listener/RabbitListenerSimple.java.ejs +56 -0
  123. package/templates/read-model/ReadModelDomain.java.ejs +46 -0
  124. package/templates/read-model/ReadModelJpa.java.ejs +58 -0
  125. package/templates/read-model/ReadModelJpaRepository.java.ejs +13 -0
  126. package/templates/read-model/ReadModelKafkaListener.java.ejs +64 -0
  127. package/templates/read-model/ReadModelRabbitListener.java.ejs +71 -0
  128. package/templates/read-model/ReadModelRepository.java.ejs +42 -0
  129. package/templates/read-model/ReadModelRepositoryImpl.java.ejs +85 -0
  130. package/templates/read-model/ReadModelSyncHandler.java.ejs +54 -0
  131. package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +18 -4
  132. package/templates/shared/configurations/rabbitmqConfig/RabbitMQConfig.java.ejs +100 -0
  133. package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +2 -64
  134. package/templates/shared/configurations/temporalConfig/TemporalWorkerFactoryLifecycle.java.ejs +41 -0
  135. package/templates/temporal-activity/ActivityImpl.java.ejs +68 -2
  136. package/templates/temporal-activity/ActivityInput.java.ejs +14 -0
  137. package/templates/temporal-activity/ActivityInterface.java.ejs +7 -1
  138. package/templates/temporal-activity/ActivityOutput.java.ejs +14 -0
  139. package/templates/temporal-activity/NestedType.java.ejs +12 -0
  140. package/templates/temporal-activity/SharedActivityInput.java.ejs +14 -0
  141. package/templates/temporal-activity/SharedActivityInterface.java.ejs +15 -0
  142. package/templates/temporal-activity/SharedActivityOutput.java.ejs +14 -0
  143. package/templates/temporal-activity/SharedNestedType.java.ejs +12 -0
  144. package/templates/temporal-flow/ModuleHeavyActivity.java.ejs +6 -0
  145. package/templates/temporal-flow/ModuleLightActivity.java.ejs +6 -0
  146. package/templates/temporal-flow/ModuleTemporalWorkerConfig.java.ejs +58 -0
  147. package/templates/temporal-flow/WorkFlowImpl.java.ejs +172 -12
  148. package/templates/temporal-flow/WorkFlowInput.java.ejs +11 -0
  149. package/templates/temporal-flow/WorkFlowInterface.java.ejs +5 -4
  150. package/templates/temporal-flow/WorkFlowService.java.ejs +42 -12
  151. package/COMMAND_EVALUATION.md +0 -911
@@ -0,0 +1,269 @@
1
+ # =============================================================================
2
+ # PROTOTYPE: system.yaml con Temporal como único mecanismo de comunicación
3
+ # =============================================================================
4
+ # Este es el formato target para generación de código con Temporal.
5
+ # eva4j leerá este archivo para generar workflows cross-module
6
+ # y contratos compartidos entre módulos.
7
+ # =============================================================================
8
+
9
+ system:
10
+ name: test-eva
11
+ groupId: com.example
12
+ javaVersion: 21
13
+ springBootVersion: 3.5.5
14
+ database: postgresql
15
+
16
+ # ─── Temporal reemplaza tanto messaging (Kafka) como sync (Feign) ────────────
17
+ orchestration:
18
+ enabled: true
19
+ engine: temporal
20
+ temporal:
21
+ target: localhost:7233
22
+ namespace: test-eva
23
+ # Task queues con prefijo de módulo (module-scoped)
24
+ # Cada módulo tiene su propio set de queues:
25
+ # {MODULE}_WORKFLOW_QUEUE — Workflows del módulo
26
+ # {MODULE}_LIGHT_TASK_QUEUE — Activities rápidas (< 30s)
27
+ # {MODULE}_HEAVY_TASK_QUEUE — Activities pesadas (hasta 2min)
28
+
29
+ modules:
30
+ - name: products
31
+ description: "Product catalog management for household goods"
32
+ exposes:
33
+ - method: GET
34
+ path: /products/{id}
35
+ useCase: GetProduct
36
+ - method: GET
37
+ path: /products
38
+ useCase: FindAllProducts
39
+ - method: POST
40
+ path: /products
41
+ useCase: CreateProduct
42
+ - method: PUT
43
+ path: /products/{id}
44
+ useCase: UpdateProduct
45
+ - method: DELETE
46
+ path: /products/{id}
47
+ useCase: DeleteProduct
48
+
49
+ - name: customers
50
+ description: "Customer registration and profile management"
51
+ exposes:
52
+ - method: GET
53
+ path: /customers/{id}
54
+ useCase: GetCustomer
55
+ - method: GET
56
+ path: /customers
57
+ useCase: FindAllCustomers
58
+ - method: POST
59
+ path: /customers
60
+ useCase: CreateCustomer
61
+ - method: PUT
62
+ path: /customers/{id}
63
+ useCase: UpdateCustomer
64
+
65
+ - name: orders
66
+ description: "Order lifecycle management from placement to completion"
67
+ exposes:
68
+ - method: POST
69
+ path: /orders
70
+ useCase: CreateOrder
71
+ - method: GET
72
+ path: /orders/{id}
73
+ useCase: GetOrder
74
+ - method: GET
75
+ path: /orders
76
+ useCase: FindAllOrders
77
+ - method: PUT
78
+ path: /orders/{id}/cancel
79
+ useCase: CancelOrder
80
+
81
+ - name: payments
82
+ description: "Payment processing and external payment gateway integration"
83
+ exposes:
84
+ - method: POST
85
+ path: /payments
86
+ useCase: CreatePayment
87
+ - method: GET
88
+ path: /payments/{id}
89
+ useCase: GetPayment
90
+ - method: GET
91
+ path: /payments
92
+ useCase: FindAllPayments
93
+ - method: POST
94
+ path: /payments/{id}/refund
95
+ useCase: RefundPayment
96
+
97
+ - name: inventory
98
+ description: "Stock level management, reservation, and replenishment"
99
+ exposes:
100
+ - method: GET
101
+ path: /inventory/{productId}
102
+ useCase: GetStock
103
+ - method: GET
104
+ path: /inventory
105
+ useCase: FindAllStocks
106
+ - method: PUT
107
+ path: /inventory/{productId}/adjust
108
+ useCase: AdjustStock
109
+ - method: POST
110
+ path: /inventory/{productId}/reserve
111
+ useCase: ReserveStock
112
+ - method: POST
113
+ path: /inventory/{productId}/release
114
+ useCase: ReleaseStock
115
+
116
+ - name: notifications
117
+ description: "Notification delivery service for email and SMS alerts"
118
+
119
+ # =============================================================================
120
+ # WORKFLOWS: Flujos de negocio cross-module orquestados por Temporal
121
+ # =============================================================================
122
+ # Solo flujos de negocio REALES — no hay workflows de sincronización de datos.
123
+ # Datos cross-module se obtienen on-demand con Remote Activities de lectura.
124
+ # Patrones usados (ver TEMPORAL_COMMUNICATION_PATTERNS.md):
125
+ # - Remote Activity: operación atómica cross-service
126
+ # - Remote Activity + Async.function(): operaciones independientes en paralelo
127
+ # =============================================================================
128
+
129
+ workflows:
130
+
131
+ # ─── Producto creado: inicializar stock en inventory ───────────────────────
132
+ # Único consumer real de ProductCreatedEvent.
133
+ # Sin read models, ya no hay steps de sincronización.
134
+ - name: ProductCreatedWorkflow
135
+ trigger:
136
+ module: products
137
+ on: create
138
+ taskQueue: PRODUCT_WORKFLOW_QUEUE
139
+ steps:
140
+ - activity: InitializeStock # Remote Activity → inventory
141
+ target: inventory
142
+ type: sync
143
+ input:
144
+ - productId
145
+ - name
146
+ compensation: DeleteStock
147
+ timeout: 10s
148
+
149
+ # ─── Orden creada: SAGA completa ───────────────────────────────────────────
150
+ # El caso estrella de Temporal — saga con compensación durable.
151
+ # Sin read models: customer y product data se obtienen on-demand
152
+ # mediante Remote Activities de lectura.
153
+ - name: PlaceOrderWorkflow
154
+ trigger:
155
+ module: orders
156
+ on: create
157
+ taskQueue: ORDER_WORKFLOW_QUEUE
158
+ saga: true
159
+ steps:
160
+ # Paso 1: Obtener detalles de la orden (Local Activity → orders)
161
+ # Lee el agregado completo: items, customerId, totalAmount.
162
+ # Permite que el workflow input sea solo orderId.
163
+ - activity: GetOrderDetails
164
+ target: orders
165
+ type: sync
166
+ input: [orderId]
167
+ output: [customerId, items, totalAmount]
168
+ timeout: 5s
169
+
170
+ # Paso 2: Obtener datos del cliente (Remote Activity → customers)
171
+ # Valida existencia y obtiene email/nombre para notificaciones.
172
+ - activity: GetCustomerById
173
+ target: customers
174
+ type: sync
175
+ input: [customerId]
176
+ output: [customerId, firstName, lastName, email, phone]
177
+ timeout: 5s
178
+
179
+ # Paso 3: Reservar stock (Remote Activity → inventory)
180
+ - activity: ReserveStock
181
+ target: inventory
182
+ type: sync
183
+ input: [orderId, items]
184
+ compensation: ReleaseStock
185
+ timeout: 10s
186
+
187
+ # Paso 4: Procesar pago (Remote Activity → payments)
188
+ - activity: ProcessOrderPayment
189
+ target: payments
190
+ type: sync
191
+ input: [orderId, customerId, totalAmount]
192
+ output: [paymentId]
193
+ compensation: RefundPayment
194
+ timeout: 30s
195
+
196
+ # Paso 5: Confirmar orden (Local Activity → orders)
197
+ - activity: ConfirmOrder
198
+ target: orders
199
+ type: sync
200
+ input: [orderId]
201
+
202
+ # Paso 6: Notificar (Remote Activity → notifications)
203
+ # ⚡ Async.function() — fire-and-forget, NO bloquea la saga.
204
+ - activity: NotifyOrderPlaced
205
+ target: notifications
206
+ type: async
207
+ input: [orderId, email, firstName, totalAmount]
208
+
209
+ # ─── Orden cancelada ──────────────────────────────────────────────────────
210
+ - name: CancelOrderWorkflow
211
+ trigger:
212
+ module: orders
213
+ on: cancel
214
+ taskQueue: ORDER_WORKFLOW_QUEUE
215
+ steps:
216
+ # Paso 1: Obtener detalles de la orden (Local Activity → orders)
217
+ - activity: GetOrderDetails
218
+ target: orders
219
+ type: sync
220
+ input: [orderId]
221
+ output: [customerId, items, paymentId]
222
+ timeout: 5s
223
+
224
+ # Paso 2: Obtener datos del cliente (para email de notificación)
225
+ - activity: GetCustomerById
226
+ target: customers
227
+ type: sync
228
+ input: [customerId]
229
+ output: [customerId, firstName, lastName, email, phone]
230
+ timeout: 5s
231
+
232
+ # Paso 3: Liberar stock reservado
233
+ - activity: ReleaseStock
234
+ target: inventory
235
+ type: sync
236
+ input: [orderId, items]
237
+ timeout: 10s
238
+
239
+ # Paso 4: Reembolsar pago (si hubo pago previo)
240
+ - activity: RefundPayment
241
+ target: payments
242
+ type: sync
243
+ input: [paymentId]
244
+ timeout: 30s
245
+ optional: true # No falla si no hubo pago
246
+
247
+ # Paso 5: Notificar cancelación (async, non-blocking)
248
+ - activity: NotifyOrderCancelled
249
+ target: notifications
250
+ type: async
251
+ input: [orderId, email, firstName]
252
+
253
+ # ─── ELIMINADOS: Workflows de sincronización de datos ──────────────────────
254
+ # Antes había 4 workflows que SOLO sincronizaban read models:
255
+ # - ProductUpdatedWorkflow → SyncProductReadModel en orders
256
+ # - ProductDeactivatedWorkflow → SoftDeleteProductReadModel en orders
257
+ # - CustomerCreatedWorkflow → SyncCustomerReadModel en orders + notifications
258
+ # - CustomerUpdatedWorkflow → SyncCustomerReadModel en orders + notifications
259
+ #
260
+ # ELIMINADOS porque:
261
+ # 1. Read models eliminados — datos se obtienen on-demand via Remote Activity
262
+ # 2. GetCustomerById reemplaza las proyecciones locales
263
+ # 3. Menos acoplamiento productor→consumidor (customers/products ya no saben
264
+ # quién los consume)
265
+
266
+ # ─── ABSORBIDOS por PlaceOrderWorkflow (saga) ─────────────────────────────
267
+ # PaymentApprovedWorkflow / PaymentFailedWorkflow → la saga integra el
268
+ # resultado de ProcessOrderPayment directamente. Si falla → compensa.
269
+ # Si aprueba → ConfirmOrder.
@@ -0,0 +1,140 @@
1
+ # EJEMPLO: ENDPOINTS — MÓDULO CON MÚLTIPLES AGREGADOS
2
+ # Módulo de productos con dos agregados: Product y Category.
3
+ #
4
+ # REGLA CLAVE:
5
+ # Cuando un módulo tiene 2+ agregados, NO se puede usar un solo
6
+ # basePath (ej: /products) porque cada agregado tiene su propio
7
+ # recurso REST. En este caso:
8
+ #
9
+ # - Usar basePath: "" (string vacío — NUNCA basePath: / con slash)
10
+ # - Declarar paths ABSOLUTOS en cada operación
11
+ # - El controlador generado tendrá @RequestMapping("/api/v1") (limpio)
12
+ #
13
+ # CONTRASTE CON MÓDULO DE UN SOLO AGREGADO:
14
+ # basePath: /orders → paths relativos: /, /{id}, /{id}/confirm
15
+ # basePath: "" → paths absolutos: /products, /categories/{id}
16
+
17
+ aggregates:
18
+ # ── Primer agregado: Product ──────────────────────────────────────────────
19
+ - name: Product
20
+ entities:
21
+ - name: product
22
+ isRoot: true
23
+ tableName: products
24
+ hasSoftDelete: true
25
+ audit:
26
+ enabled: true
27
+ trackUser: true
28
+ fields:
29
+ - name: id
30
+ type: String
31
+ - name: name
32
+ type: String
33
+ validations:
34
+ - type: NotBlank
35
+ message: "Product name is required"
36
+ - name: description
37
+ type: String
38
+ - name: price
39
+ type: BigDecimal
40
+ validations:
41
+ - type: Positive
42
+ - name: categoryId
43
+ type: String
44
+ reference:
45
+ aggregate: Category
46
+ - name: status
47
+ type: ProductStatus
48
+ readOnly: true
49
+
50
+ enums:
51
+ - name: ProductStatus
52
+ initialValue: DRAFT
53
+ transitions:
54
+ - from: DRAFT
55
+ to: PUBLISHED
56
+ method: publish
57
+ - from: PUBLISHED
58
+ to: ARCHIVED
59
+ method: archive
60
+ values: [DRAFT, PUBLISHED, ARCHIVED]
61
+
62
+ events:
63
+ - name: ProductCreatedEvent
64
+ lifecycle: create
65
+ fields:
66
+ - name: productId
67
+ type: String
68
+ - name: name
69
+ type: String
70
+ - name: price
71
+ type: BigDecimal
72
+ - name: ProductPublishedEvent
73
+ triggers:
74
+ - publish
75
+ fields:
76
+ - name: productId
77
+ type: String
78
+ - name: publishedAt
79
+ type: LocalDateTime
80
+
81
+ # ── Segundo agregado: Category ────────────────────────────────────────────
82
+ - name: Category
83
+ entities:
84
+ - name: category
85
+ isRoot: true
86
+ tableName: categories
87
+ audit:
88
+ enabled: true
89
+ fields:
90
+ - name: id
91
+ type: String
92
+ - name: name
93
+ type: String
94
+ validations:
95
+ - type: NotBlank
96
+ message: "Category name is required"
97
+ - name: description
98
+ type: String
99
+
100
+ # ── Endpoints: basePath vacío + paths absolutos ─────────────────────────────
101
+ # NOTA: basePath: "" (vacío) porque hay 2 agregados.
102
+ # Cada path es absoluto: /products, /categories, etc.
103
+ endpoints:
104
+ basePath: ""
105
+ versions:
106
+ - version: v1
107
+ operations:
108
+ # ── Product operations ──
109
+ - useCase: CreateProduct
110
+ method: POST
111
+ path: /products
112
+ - useCase: UpdateProduct
113
+ method: PUT
114
+ path: /products/{id}
115
+ - useCase: GetProduct
116
+ method: GET
117
+ path: /products/{id}
118
+ - useCase: FindAllProducts
119
+ method: GET
120
+ path: /products
121
+ - useCase: PublishProduct
122
+ method: PUT
123
+ path: /products/{id}/publish
124
+ # ── Category operations ──
125
+ - useCase: CreateCategory
126
+ method: POST
127
+ path: /categories
128
+ - useCase: UpdateCategory
129
+ method: PUT
130
+ path: /categories/{id}
131
+ - useCase: GetCategory
132
+ method: GET
133
+ path: /categories/{id}
134
+ - useCase: FindAllCategories
135
+ method: GET
136
+ path: /categories
137
+ # ── Cross-aggregate query ──
138
+ - useCase: FindProductsByCategory
139
+ method: GET
140
+ path: /categories/{id}/products
@@ -305,6 +305,32 @@ aggregates:
305
305
  - name: estimatedDeliveryDate # no resuelto → null /* TODO */
306
306
  type: LocalDate
307
307
 
308
+ # ─── Eventos con lifecycle: (alterntiva a triggers:) ──────────────
309
+ # Cuando el evento se produce en una operación CRUD y no en una transición de estado,
310
+ # se usa lifecycle: en lugar de triggers:
311
+ # El generador emite raise() automáticamente en el punto CRUD correspondiente.
312
+
313
+ - name: OrderCreatedEvent
314
+ lifecycle: create # raise() en el constructor de creación
315
+ fields: # UUID auto-generado como id antes de raise()
316
+ - name: orderId
317
+ type: String
318
+ - name: customerName # → this.getCustomerName() si existe en la entidad
319
+ type: String
320
+ - name: createdAt
321
+ type: LocalDateTime # → LocalDateTime.now()
322
+
323
+ - name: OrderUpdatedEvent
324
+ lifecycle: update # raise() en el método update() de la entidad raíz
325
+ fields:
326
+ - name: orderId
327
+ type: String
328
+ - name: customerName
329
+ type: String
330
+
331
+ # lifecycle: delete requiere hasSoftDelete: false en la entidad raíz
332
+ # lifecycle: softDelete requiere hasSoftDelete: true en la entidad raíz
333
+
308
334
  # ─── Eventos externos que este módulo CONSUME ────────────────────────────────
309
335
  # Nivel raíz, sibling de aggregates:
310
336
  # Requiere broker instalado (eva add kafka-client) para generación.
@@ -0,0 +1,113 @@
1
+ # Example: Read Models (Local Projections)
2
+ #
3
+ # This example shows an "orders" module that uses readModels to maintain
4
+ # local projections of data from "products" and "customers" modules,
5
+ # eliminating synchronous HTTP dependencies.
6
+ #
7
+ # ─── SOURCE MODULE NOTE ──────────────────────────────────────────────────────
8
+ # The source module (e.g. products/domain.yaml) must declare the events
9
+ # that this readModel's syncedBy entries reference. Use lifecycle: to
10
+ # automatically emit raise() at CRUD operations:
11
+ #
12
+ # events:
13
+ # - name: ProductCreatedEvent
14
+ # lifecycle: create # raise() in creation constructor
15
+ # fields:
16
+ # - name: productId
17
+ # type: String
18
+ # - name: name
19
+ # type: String
20
+ # - name: price
21
+ # type: BigDecimal
22
+ # - name: status
23
+ # type: String
24
+ #
25
+ # - name: ProductUpdatedEvent
26
+ # lifecycle: update # raise() in UpdateCommandHandler
27
+ # fields: [...]
28
+ #
29
+ # - name: ProductDeactivatedEvent
30
+ # lifecycle: softDelete # raise() in softDelete() method
31
+ # fields: [...]
32
+ # ─────────────────────────────────────────────────────────────────────────────
33
+
34
+ aggregates:
35
+ - name: Order
36
+ entities:
37
+ - name: Order
38
+ isRoot: true
39
+ tableName: orders
40
+ audit:
41
+ enabled: true
42
+ trackUser: true
43
+ fields:
44
+ - name: id
45
+ type: String
46
+ - name: orderNumber
47
+ type: String
48
+ - name: customerId
49
+ type: String
50
+ reference:
51
+ aggregate: Customer
52
+ module: customers
53
+ - name: totalAmount
54
+ type: BigDecimal
55
+ readOnly: true
56
+ defaultValue: "0.00"
57
+ enums:
58
+ - name: OrderStatus
59
+ initialValue: DRAFT
60
+ values: [DRAFT, PLACED, CONFIRMED, CANCELLED]
61
+ transitions:
62
+ - from: DRAFT
63
+ to: PLACED
64
+ method: place
65
+ - from: PLACED
66
+ to: CONFIRMED
67
+ method: confirm
68
+ - from: [DRAFT, PLACED]
69
+ to: CANCELLED
70
+ method: cancel
71
+
72
+ readModels:
73
+ - name: ProductReadModel
74
+ source:
75
+ module: products
76
+ aggregate: Product
77
+ tableName: rm_orders_products
78
+ fields:
79
+ - name: id
80
+ type: String
81
+ - name: name
82
+ type: String
83
+ - name: price
84
+ type: BigDecimal
85
+ - name: status
86
+ type: String
87
+ syncedBy:
88
+ - event: ProductCreatedEvent
89
+ action: UPSERT
90
+ - event: ProductUpdatedEvent
91
+ action: UPSERT
92
+ - event: ProductDeactivatedEvent
93
+ action: SOFT_DELETE
94
+
95
+ - name: CustomerReadModel
96
+ source:
97
+ module: customers
98
+ aggregate: Customer
99
+ tableName: rm_orders_customers
100
+ fields:
101
+ - name: id
102
+ type: String
103
+ - name: fullName
104
+ type: String
105
+ - name: email
106
+ type: String
107
+ syncedBy:
108
+ - event: CustomerRegisteredEvent
109
+ action: UPSERT
110
+ - event: CustomerUpdatedEvent
111
+ action: UPSERT
112
+ - event: CustomerDeletedEvent
113
+ action: DELETE
@@ -0,0 +1,89 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # customer — Domain Model
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Gestión de clientes. Emite eventos de ciclo de vida (create, update, delete)
5
+ # para que otros módulos mantengan proyecciones locales via readModels.
6
+ # ─────────────────────────────────────────────────────────────────────────────
7
+
8
+ aggregates:
9
+ - name: Customer
10
+ entities:
11
+ - name: Customer
12
+ isRoot: true
13
+ tableName: customers
14
+ audit:
15
+ enabled: true
16
+ trackUser: true
17
+ fields:
18
+ - name: id
19
+ type: String
20
+ - name: fullName
21
+ type: String
22
+ validations:
23
+ - type: NotBlank
24
+ message: "Full name is required"
25
+ - name: email
26
+ type: String
27
+ validations:
28
+ - type: NotBlank
29
+ message: "Email is required"
30
+ - type: Email
31
+ message: "Invalid email format"
32
+ - name: phone
33
+ type: String
34
+
35
+ events:
36
+ # CustomerCreatedEvent — lifecycle: create → raise() in creation constructor
37
+ - name: CustomerCreatedEvent
38
+ lifecycle: create
39
+ fields:
40
+ - name: customerId
41
+ type: String
42
+ - name: fullName
43
+ type: String
44
+ - name: email
45
+ type: String
46
+
47
+ # CustomerUpdatedEvent — lifecycle: update → raise() in update() method
48
+ - name: CustomerUpdatedEvent
49
+ lifecycle: update
50
+ fields:
51
+ - name: customerId
52
+ type: String
53
+ - name: fullName
54
+ type: String
55
+ - name: email
56
+ type: String
57
+
58
+ # CustomerDeletedEvent — lifecycle: delete → raise() in DeleteCommandHandler
59
+ - name: CustomerDeletedEvent
60
+ lifecycle: delete
61
+ fields:
62
+ - name: customerId
63
+ type: String
64
+ - name: deletedAt
65
+ type: LocalDateTime
66
+
67
+ # ─────────────────────────────────────────────────────────────────────────────
68
+ # Declarative REST endpoints
69
+ # ─────────────────────────────────────────────────────────────────────────────
70
+ endpoints:
71
+ basePath: /customers
72
+ versions:
73
+ - version: v1
74
+ operations:
75
+ - useCase: CreateCustomer
76
+ method: POST
77
+ path: /
78
+ - useCase: GetCustomer
79
+ method: GET
80
+ path: /{id}
81
+ - useCase: FindAllCustomers
82
+ method: GET
83
+ path: /
84
+ - useCase: UpdateCustomer
85
+ method: PUT
86
+ path: /{id}
87
+ - useCase: DeleteCustomer
88
+ method: DELETE
89
+ path: /{id}