eva4j 1.0.17 → 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 (134) hide show
  1. package/AGENTS.md +2 -0
  2. package/DOMAIN_YAML_GUIDE.md +3 -1
  3. package/QUICK_REFERENCE.md +8 -4
  4. package/bin/eva4j.js +70 -2
  5. package/config/defaults.json +1 -0
  6. package/docs/CAMUNDA_DMN_GUIDE.md +1380 -0
  7. package/docs/KAFKA_PRODUCTION_CONFIG.md +441 -0
  8. package/docs/RABBITMQ_PRODUCTION_CONFIG.md +227 -0
  9. package/docs/commands/ADD_RABBITMQ_CLIENT.md +192 -0
  10. package/docs/commands/EVALUATE_SYSTEM.md +272 -8
  11. package/docs/commands/GENERATE_RABBITMQ_EVENT.md +341 -0
  12. package/docs/commands/GENERATE_RABBITMQ_LISTENER.md +595 -0
  13. package/docs/commands/GENERATE_TEMPORAL_FLOW.md +52 -12
  14. package/docs/commands/INDEX.md +27 -3
  15. package/docs/prototype/TEMPORAL_COMMUNICATION_PATTERNS.md +731 -0
  16. package/docs/prototype/TEMPORAL_DESIGN_METHODOLOGY.md +740 -0
  17. package/docs/prototype/system/RISKS.md +277 -0
  18. package/docs/prototype/system/customers.yaml +133 -0
  19. package/docs/prototype/system/inventory.yaml +109 -0
  20. package/docs/prototype/system/notifications.yaml +131 -0
  21. package/docs/prototype/system/orders.yaml +241 -0
  22. package/docs/prototype/system/payments.yaml +256 -0
  23. package/docs/prototype/system/products.yaml +168 -0
  24. package/docs/prototype/system/system.yaml +269 -0
  25. package/examples/domain-endpoints-multi-aggregate.yaml +140 -0
  26. package/examples/domain-read-models.yaml +2 -2
  27. package/examples/system/customer.yaml +89 -0
  28. package/examples/system/orders.yaml +119 -0
  29. package/examples/system/product.yaml +27 -0
  30. package/examples/system/system.yaml +80 -0
  31. package/package.json +1 -1
  32. package/src/agents/design-gap-analyst-temporal.agent.md +452 -0
  33. package/src/agents/design-gap-analyst.agent.md +383 -0
  34. package/src/agents/design-reviewer-temporal.agent.md +412 -0
  35. package/src/agents/design-reviewer.agent.md +31 -5
  36. package/src/agents/implement-use-cases.prompt.md +179 -0
  37. package/src/agents/ux-gap-analyst.agent.md +412 -0
  38. package/src/commands/add-rabbitmq-client.js +261 -0
  39. package/src/commands/add-temporal-client.js +22 -2
  40. package/src/commands/build.js +267 -11
  41. package/src/commands/evaluate-system.js +700 -13
  42. package/src/commands/generate-entities.js +308 -16
  43. package/src/commands/generate-rabbitmq-event.js +665 -0
  44. package/src/commands/generate-rabbitmq-listener.js +205 -0
  45. package/src/commands/generate-temporal-activity.js +968 -34
  46. package/src/commands/generate-temporal-flow.js +95 -38
  47. package/src/commands/generate-temporal-system.js +708 -0
  48. package/src/skills/build-system-yaml/SKILL.md +222 -2
  49. package/src/skills/build-system-yaml/references/domain-yaml-spec.md +50 -4
  50. package/src/skills/build-system-yaml/references/module-spec.md +57 -8
  51. package/src/skills/build-temporal-system/SKILL.md +752 -0
  52. package/src/skills/build-temporal-system/references/temporal-communication-patterns.md +167 -0
  53. package/src/skills/build-temporal-system/references/temporal-domain-yaml-spec.md +449 -0
  54. package/src/skills/build-temporal-system/references/temporal-module-spec.md +353 -0
  55. package/src/skills/build-temporal-system/references/temporal-system-yaml-spec.md +326 -0
  56. package/src/skills/implement-use-case/SKILL.md +350 -0
  57. package/src/skills/implement-use-case/references/use-case-patterns.md +980 -0
  58. package/src/skills/requirements-elicitation/SKILL.md +228 -0
  59. package/src/skills/requirements-elicitation/references/interview-framework.md +260 -0
  60. package/src/skills/requirements-elicitation/references/output-templates.md +368 -0
  61. package/src/utils/bounded-context-diagram.js +844 -0
  62. package/src/utils/domain-validator.js +266 -4
  63. package/src/utils/naming.js +10 -0
  64. package/src/utils/system-validator.js +169 -11
  65. package/src/utils/system-yaml-parser.js +318 -0
  66. package/src/utils/temporal-validator.js +497 -0
  67. package/src/utils/yaml-to-entity.js +10 -7
  68. package/templates/aggregate/DomainEventHandler.java.ejs +116 -22
  69. package/templates/aggregate/JpaAggregateRoot.java.ejs +2 -2
  70. package/templates/aggregate/JpaEntity.java.ejs +2 -2
  71. package/templates/base/docker/rabbitmq-services.yaml.ejs +12 -0
  72. package/templates/base/resources/parameters/develop/kafka.yaml.ejs +5 -0
  73. package/templates/base/resources/parameters/develop/rabbitmq.yaml.ejs +15 -0
  74. package/templates/base/resources/parameters/develop/temporal.yaml.ejs +0 -3
  75. package/templates/base/resources/parameters/local/kafka.yaml.ejs +5 -0
  76. package/templates/base/resources/parameters/local/rabbitmq.yaml.ejs +15 -0
  77. package/templates/base/resources/parameters/local/temporal.yaml.ejs +0 -3
  78. package/templates/base/resources/parameters/production/kafka.yaml.ejs +39 -8
  79. package/templates/base/resources/parameters/production/rabbitmq.yaml.ejs +32 -0
  80. package/templates/base/resources/parameters/production/temporal.yaml.ejs +0 -3
  81. package/templates/base/resources/parameters/test/kafka.yaml.ejs +12 -6
  82. package/templates/base/resources/parameters/test/rabbitmq.yaml.ejs +15 -0
  83. package/templates/base/resources/parameters/test/temporal.yaml.ejs +0 -3
  84. package/templates/base/root/AGENTS.md.ejs +1 -1
  85. package/templates/crud/EndpointsController.java.ejs +1 -1
  86. package/templates/crud/ScaffoldCommand.java.ejs +5 -2
  87. package/templates/crud/ScaffoldCommandHandler.java.ejs +3 -1
  88. package/templates/crud/ScaffoldQuery.java.ejs +5 -2
  89. package/templates/crud/ScaffoldQueryHandler.java.ejs +3 -1
  90. package/templates/crud/SubEntityRemoveCommand.java.ejs +1 -1
  91. package/templates/evaluate/report.html.ejs +1447 -90
  92. package/templates/kafka-event/KafkaConfigBean.java.ejs +1 -1
  93. package/templates/kafka-event/KafkaMessageBroker.java.ejs +3 -3
  94. package/templates/ports/PortAclMapper.java.ejs +35 -0
  95. package/templates/ports/PortFeignAdapter.java.ejs +7 -22
  96. package/templates/ports/PortFeignClient.java.ejs +4 -0
  97. package/templates/ports/PortResponseDto.java.ejs +1 -1
  98. package/templates/rabbitmq-event/RabbitConfigBean.java.ejs +33 -0
  99. package/templates/rabbitmq-event/RabbitConfigExchange.java.ejs +12 -0
  100. package/templates/rabbitmq-event/RabbitMessageBroker.java.ejs +35 -0
  101. package/templates/rabbitmq-event/RabbitMessageBrokerMethod.java.ejs +9 -0
  102. package/templates/rabbitmq-listener/RabbitConfigConsumerBean.java.ejs +33 -0
  103. package/templates/rabbitmq-listener/RabbitConfigConsumerExchange.java.ejs +12 -0
  104. package/templates/rabbitmq-listener/RabbitListenerClass.java.ejs +82 -0
  105. package/templates/rabbitmq-listener/RabbitListenerSimple.java.ejs +56 -0
  106. package/templates/read-model/ReadModelJpa.java.ejs +1 -1
  107. package/templates/read-model/ReadModelJpaRepository.java.ejs +2 -0
  108. package/templates/read-model/ReadModelRabbitListener.java.ejs +71 -0
  109. package/templates/read-model/ReadModelRepositoryImpl.java.ejs +9 -5
  110. package/templates/read-model/ReadModelSyncHandler.java.ejs +2 -0
  111. package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +18 -4
  112. package/templates/shared/configurations/rabbitmqConfig/RabbitMQConfig.java.ejs +100 -0
  113. package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +2 -64
  114. package/templates/shared/configurations/temporalConfig/TemporalWorkerFactoryLifecycle.java.ejs +41 -0
  115. package/templates/temporal-activity/ActivityImpl.java.ejs +68 -2
  116. package/templates/temporal-activity/ActivityInput.java.ejs +14 -0
  117. package/templates/temporal-activity/ActivityInterface.java.ejs +7 -1
  118. package/templates/temporal-activity/ActivityOutput.java.ejs +14 -0
  119. package/templates/temporal-activity/NestedType.java.ejs +12 -0
  120. package/templates/temporal-activity/SharedActivityInput.java.ejs +14 -0
  121. package/templates/temporal-activity/SharedActivityInterface.java.ejs +15 -0
  122. package/templates/temporal-activity/SharedActivityOutput.java.ejs +14 -0
  123. package/templates/temporal-activity/SharedNestedType.java.ejs +12 -0
  124. package/templates/temporal-flow/ModuleHeavyActivity.java.ejs +6 -0
  125. package/templates/temporal-flow/ModuleLightActivity.java.ejs +6 -0
  126. package/templates/temporal-flow/ModuleTemporalWorkerConfig.java.ejs +58 -0
  127. package/templates/temporal-flow/WorkFlowImpl.java.ejs +172 -12
  128. package/templates/temporal-flow/WorkFlowInput.java.ejs +11 -0
  129. package/templates/temporal-flow/WorkFlowInterface.java.ejs +5 -4
  130. package/templates/temporal-flow/WorkFlowService.java.ejs +42 -12
  131. package/COMMAND_EVALUATION.md +0 -911
  132. package/test-c2010.js +0 -49
  133. package/test-update-compat.js +0 -109
  134. package/test-update-lifecycle.js +0 -121
@@ -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
@@ -74,7 +74,7 @@ readModels:
74
74
  source:
75
75
  module: products
76
76
  aggregate: Product
77
- tableName: rm_products
77
+ tableName: rm_orders_products
78
78
  fields:
79
79
  - name: id
80
80
  type: String
@@ -96,7 +96,7 @@ readModels:
96
96
  source:
97
97
  module: customers
98
98
  aggregate: Customer
99
- tableName: rm_customers
99
+ tableName: rm_orders_customers
100
100
  fields:
101
101
  - name: id
102
102
  type: String
@@ -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}
@@ -0,0 +1,119 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # orders — Domain Model
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Gestión de órdenes de compra. Usa readModels para mantener proyecciones
5
+ # locales de Product y Customer, eliminando dependencias HTTP síncronas.
6
+ # ─────────────────────────────────────────────────────────────────────────────
7
+
8
+ aggregates:
9
+ - name: Order
10
+ entities:
11
+ - name: Order
12
+ isRoot: true
13
+ tableName: orders
14
+ audit:
15
+ enabled: true
16
+ trackUser: true
17
+ fields:
18
+ - name: id
19
+ type: String
20
+ - name: orderNumber
21
+ type: String
22
+ validations:
23
+ - type: NotBlank
24
+ message: "Order number is required"
25
+ - name: customerId
26
+ type: String
27
+ reference:
28
+ aggregate: Customer
29
+ module: customer
30
+ validations:
31
+ - type: NotBlank
32
+ message: "Customer ID is required"
33
+ - name: productId
34
+ type: String
35
+ reference:
36
+ aggregate: Product
37
+ module: product
38
+ validations:
39
+ - type: NotBlank
40
+ message: "Product ID is required"
41
+ - name: quantity
42
+ type: Integer
43
+ validations:
44
+ - type: Positive
45
+ - name: totalAmount
46
+ type: BigDecimal
47
+ readOnly: true
48
+ defaultValue: "0.00"
49
+
50
+ # ─────────────────────────────────────────────────────────────────────────────
51
+ # Declarative REST endpoints
52
+ # ─────────────────────────────────────────────────────────────────────────────
53
+ endpoints:
54
+ basePath: /orders
55
+ versions:
56
+ - version: v1
57
+ operations:
58
+ - useCase: CreateOrder
59
+ method: POST
60
+ path: /
61
+ - useCase: GetOrder
62
+ method: GET
63
+ path: /{id}
64
+ - useCase: FindAllOrders
65
+ method: GET
66
+ path: /
67
+ - useCase: UpdateOrder
68
+ method: PUT
69
+ path: /{id}
70
+ - useCase: DeleteOrder
71
+ method: DELETE
72
+ path: /{id}
73
+
74
+ # ─────────────────────────────────────────────────────────────────────────────
75
+ # Read Models — proyecciones locales de datos de otros módulos
76
+ # ─────────────────────────────────────────────────────────────────────────────
77
+ # Cada readModel mantiene una tabla local (rm_*) sincronizada por eventos de
78
+ # ciclo de vida del módulo productor. Elimina la necesidad de ports: (HTTP).
79
+ # ─────────────────────────────────────────────────────────────────────────────
80
+ readModels:
81
+ - name: ProductReadModel
82
+ source:
83
+ module: product
84
+ aggregate: Product
85
+ tableName: rm_orders_products
86
+ fields:
87
+ - name: id
88
+ type: String
89
+ - name: name
90
+ type: String
91
+ - name: price
92
+ type: BigDecimal
93
+ - name: categoryId
94
+ type: String
95
+ syncedBy:
96
+ - event: ProductCreatedEvent
97
+ action: UPSERT
98
+ - event: ProductUpdatedEvent
99
+ action: UPSERT
100
+
101
+ - name: CustomerReadModel
102
+ source:
103
+ module: customer
104
+ aggregate: Customer
105
+ tableName: rm_orders_customers
106
+ fields:
107
+ - name: id
108
+ type: String
109
+ - name: fullName
110
+ type: String
111
+ - name: email
112
+ type: String
113
+ syncedBy:
114
+ - event: CustomerCreatedEvent
115
+ action: UPSERT
116
+ - event: CustomerUpdatedEvent
117
+ action: UPSERT
118
+ - event: CustomerDeletedEvent
119
+ action: DELETE
@@ -80,6 +80,33 @@ aggregates:
80
80
  - DISCONTINUED
81
81
 
82
82
  events:
83
+ # ProductCreatedEvent — lifecycle: create → raise() in creation constructor
84
+ # Feeds readModels in consumer modules (e.g. orders/ProductReadModel)
85
+ - name: ProductCreatedEvent
86
+ lifecycle: create
87
+ fields:
88
+ - name: productId
89
+ type: String
90
+ - name: name
91
+ type: String
92
+ - name: price
93
+ type: BigDecimal
94
+ - name: categoryId
95
+ type: String
96
+
97
+ # ProductUpdatedEvent — lifecycle: update → raise() in update() method
98
+ - name: ProductUpdatedEvent
99
+ lifecycle: update
100
+ fields:
101
+ - name: productId
102
+ type: String
103
+ - name: name
104
+ type: String
105
+ - name: price
106
+ type: BigDecimal
107
+ - name: categoryId
108
+ type: String
109
+
83
110
  # ProductPublishedEvent — triggered by publish transition.
84
111
  # price: BigDecimal tests VO-unwrap: entity.price is Price, event expects BigDecimal
85
112
  # → generator should emit: this.getPrice().getAmount()