eva4j 1.0.13 → 1.0.15

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 (106) hide show
  1. package/AGENTS.md +314 -10
  2. package/COMMAND_EVALUATION.md +15 -16
  3. package/DOMAIN_YAML_GUIDE.md +576 -10
  4. package/FUTURE_FEATURES.md +1627 -1168
  5. package/README.md +318 -13
  6. package/bin/eva4j.js +34 -0
  7. package/config/defaults.json +1 -0
  8. package/design-system.md +797 -0
  9. package/docs/commands/EVALUATE_SYSTEM.md +994 -0
  10. package/docs/commands/GENERATE_ENTITIES.md +795 -6
  11. package/docs/commands/INDEX.md +10 -1
  12. package/examples/domain-endpoints-relations.yaml +353 -0
  13. package/examples/domain-endpoints-versioned.yaml +144 -0
  14. package/examples/domain-endpoints.yaml +135 -0
  15. package/examples/domain-events.yaml +166 -20
  16. package/examples/domain-listeners.yaml +212 -0
  17. package/examples/domain-one-to-many.yaml +1 -0
  18. package/examples/domain-one-to-one.yaml +1 -0
  19. package/examples/domain-ports.yaml +414 -0
  20. package/examples/domain-soft-delete.yaml +47 -44
  21. package/examples/system/notification.yaml +147 -0
  22. package/examples/system/product.yaml +185 -0
  23. package/examples/system/system.yaml +112 -0
  24. package/examples/system-report.html +971 -0
  25. package/examples/system.yaml +332 -0
  26. package/package.json +2 -1
  27. package/src/commands/build.js +714 -0
  28. package/src/commands/create.js +7 -3
  29. package/src/commands/detach.js +1 -0
  30. package/src/commands/evaluate-system.js +610 -0
  31. package/src/commands/generate-entities.js +1331 -49
  32. package/src/commands/generate-http-exchange.js +2 -0
  33. package/src/commands/generate-kafka-event.js +98 -11
  34. package/src/generators/base-generator.js +8 -1
  35. package/src/generators/postman-generator.js +188 -0
  36. package/src/generators/shared-generator.js +10 -0
  37. package/src/utils/config-manager.js +54 -0
  38. package/src/utils/context-builder.js +1 -0
  39. package/src/utils/domain-diagram.js +192 -0
  40. package/src/utils/domain-validator.js +970 -0
  41. package/src/utils/fake-data.js +376 -0
  42. package/src/utils/naming.js +3 -2
  43. package/src/utils/system-validator.js +434 -0
  44. package/src/utils/yaml-to-entity.js +302 -8
  45. package/templates/aggregate/AggregateMapper.java.ejs +3 -2
  46. package/templates/aggregate/AggregateRepository.java.ejs +8 -2
  47. package/templates/aggregate/AggregateRepositoryImpl.java.ejs +13 -3
  48. package/templates/aggregate/AggregateRoot.java.ejs +60 -2
  49. package/templates/aggregate/DomainEventHandler.java.ejs +27 -20
  50. package/templates/aggregate/DomainEventRecord.java.ejs +24 -8
  51. package/templates/aggregate/DomainEventSnapshot.java.ejs +46 -0
  52. package/templates/aggregate/JpaAggregateRoot.java.ejs +6 -0
  53. package/templates/aggregate/JpaRepository.java.ejs +5 -0
  54. package/templates/base/gradle/build.gradle.ejs +3 -2
  55. package/templates/base/root/AGENTS.md.ejs +306 -45
  56. package/templates/base/root/skill-build-domain-yaml-references-generate-entities.md.ejs +1663 -0
  57. package/templates/base/root/skill-build-system-yaml.ejs +1446 -0
  58. package/templates/base/root/system.yaml.ejs +97 -0
  59. package/templates/crud/ApplicationMapper.java.ejs +4 -0
  60. package/templates/crud/Controller.java.ejs +4 -4
  61. package/templates/crud/CreateCommand.java.ejs +4 -0
  62. package/templates/crud/CreateItemDto.java.ejs +4 -0
  63. package/templates/crud/CreateValueObjectDto.java.ejs +4 -0
  64. package/templates/crud/DeleteCommandHandler.java.ejs +10 -2
  65. package/templates/crud/EndpointsController.java.ejs +178 -0
  66. package/templates/crud/FindByQuery.java.ejs +17 -0
  67. package/templates/crud/FindByQueryHandler.java.ejs +57 -0
  68. package/templates/crud/ListQuery.java.ejs +1 -1
  69. package/templates/crud/ListQueryHandler.java.ejs +8 -8
  70. package/templates/crud/ScaffoldCommand.java.ejs +12 -0
  71. package/templates/crud/ScaffoldCommandHandler.java.ejs +43 -0
  72. package/templates/crud/ScaffoldQuery.java.ejs +13 -0
  73. package/templates/crud/ScaffoldQueryHandler.java.ejs +41 -0
  74. package/templates/crud/SubEntityAddCommand.java.ejs +21 -0
  75. package/templates/crud/SubEntityAddCommandHandler.java.ejs +43 -0
  76. package/templates/crud/SubEntityRemoveCommand.java.ejs +9 -0
  77. package/templates/crud/SubEntityRemoveCommandHandler.java.ejs +42 -0
  78. package/templates/crud/TransitionCommand.java.ejs +9 -0
  79. package/templates/crud/TransitionCommandHandler.java.ejs +39 -0
  80. package/templates/crud/UpdateCommand.java.ejs +4 -0
  81. package/templates/evaluate/report.html.ejs +1363 -0
  82. package/templates/kafka-event/DomainEventHandlerMethod.ejs +3 -1
  83. package/templates/kafka-event/Event.java.ejs +16 -0
  84. package/templates/kafka-listener/KafkaController.java.ejs +1 -1
  85. package/templates/kafka-listener/KafkaListenerClass.java.ejs +1 -1
  86. package/templates/kafka-listener/ListenerClass.java.ejs +65 -0
  87. package/templates/kafka-listener/ListenerCommand.java.ejs +31 -0
  88. package/templates/kafka-listener/ListenerCommandHandler.java.ejs +23 -0
  89. package/templates/kafka-listener/ListenerIntegrationEvent.java.ejs +37 -0
  90. package/templates/kafka-listener/ListenerMethod.java.ejs +1 -1
  91. package/templates/kafka-listener/ListenerNestedType.java.ejs +28 -0
  92. package/templates/mock/MockEvent.java.ejs +10 -0
  93. package/templates/mock/MockMessageBrokerImpl.java.ejs +35 -0
  94. package/templates/mock/MockMessageBrokerImplMethod.java.ejs +6 -0
  95. package/templates/mock/SpringEventListener.java.ejs +61 -0
  96. package/templates/ports/PortDomainModel.java.ejs +35 -0
  97. package/templates/ports/PortFeignAdapter.java.ejs +67 -0
  98. package/templates/ports/PortFeignClient.java.ejs +45 -0
  99. package/templates/ports/PortFeignConfig.java.ejs +24 -0
  100. package/templates/ports/PortInterface.java.ejs +45 -0
  101. package/templates/ports/PortNestedType.java.ejs +28 -0
  102. package/templates/ports/PortRequestDto.java.ejs +30 -0
  103. package/templates/ports/PortResponseDto.java.ejs +28 -0
  104. package/templates/postman/Collection.json.ejs +1 -1
  105. package/templates/postman/UnifiedCollection.json.ejs +185 -0
  106. package/templates/shared/configurations/eventPublicationConfig/EventPublicationSchemaConfig.java.ejs +109 -0
@@ -112,7 +112,16 @@ Commands for transitioning from monolith to microservices.
112
112
  - Service independence
113
113
  - Zero code rewrite
114
114
 
115
- ### 📊 Utility Commands
115
+ ### 📊 Analysis & Utility Commands
116
+
117
+ - **[evaluate system](./EVALUATE_SYSTEM.md)** - Statically analyze a system.yaml for architectural problems
118
+ - Referential integrity (producers, consumers, sync endpoints)
119
+ - Synchronous cycle detection (DFS — deadlock prevention)
120
+ - Module role analysis (isolation, pure consumers, autonomous modules)
121
+ - Behavior gap detection (scheduler verbs with no trigger)
122
+ - Coupling pattern analysis (asymmetric sync↔async dependencies)
123
+ - Interactive HTML report with flow simulator + network diagram
124
+ - Domain-agnostic: works for any microservices design
116
125
 
117
126
  - **info** - Display project information
118
127
  - *Documentation coming soon*
@@ -0,0 +1,353 @@
1
+ # EJEMPLO: ENDPOINTS + RELACIONES
2
+ #
3
+ # Muestra cómo funciona la sección endpoints: cuando el agregado tiene
4
+ # - Entidad raíz : Order
5
+ # - OneToMany : OrderItem
6
+ # - OneToOne : ShippingAddress
7
+ #
8
+ # REGLA CLAVE:
9
+ # La sección `endpoints:` controla CUÁLES use cases se generan.
10
+ # La sección `aggregates:` controla LA ESTRUCTURA de cada use case
11
+ # (campos, listas anidadas, DTOs de relaciones).
12
+ #
13
+ # Ambas secciones son INDEPENDIENTES entre sí. El generador combina
14
+ # la información de ambas para producir el código correcto.
15
+ #
16
+ # ─────────────────────────────────────────────────────────────────────────
17
+ # QUÉ SE GENERA
18
+ # ─────────────────────────────────────────────────────────────────────────
19
+ #
20
+ # CreateOrder → implementación COMPLETA
21
+ # Porque su nombre coincide con el patrón estándar Create{Aggregate}.
22
+ # El Command incluirá automáticamente:
23
+ # - Los campos simples de Order (orderNumber, customerId)
24
+ # - List<CreateOrderItemDto> items ← por la relación OneToMany
25
+ # - CreateShippingAddressDto shipping ← por la relación OneToOne
26
+ #
27
+ # GetOrder / FindAllOrders → implementación COMPLETA
28
+ # El ResponseDto incluirá:
29
+ # - Campos de Order
30
+ # - List<OrderItemDto> items ← OneToMany en respuesta
31
+ # - ShippingAddressDto shippingAddress ← OneToOne en respuesta
32
+ #
33
+ # ConfirmOrder / CancelOrder / ShipOrder → Cat. 1 TRANSITION
34
+ # Porque su nombre es PascalCase(method) + Aggregate para una transición
35
+ # declarada en enums[].transitions. Handler completo generado automáticamente.
36
+ #
37
+ # AddOrderItem / RemoveOrderItem → Cat. 2 SUB-ENTITY ADD/REMOVE
38
+ # Porque su nombre es Add{Target} / Remove{Target} para una relación OneToMany.
39
+ # Handler completo generado automáticamente.
40
+ #
41
+ # FindAllOrdersByCustomerId → Cat. 3 FIND-BY
42
+ # Porque su nombre sigue FindAll{Aggregate}sBy{FieldPascal} para un campo de la raíz.
43
+ # Query + handler + método en el repositorio generados automáticamente.
44
+ #
45
+ # ProcessOrderPayment → SCAFFOLD (nombre libre)
46
+ # No coincide con ningún patrón → genera stubs con TODO.
47
+ # El desarrollador añade los campos al Command e implementa el Handler.
48
+
49
+ aggregates:
50
+ - name: Order
51
+ entities:
52
+
53
+ # ── Raíz del agregado ──────────────────────────────────────────────
54
+ - name: Order
55
+ isRoot: true
56
+ tableName: orders
57
+ audit:
58
+ enabled: true
59
+ trackUser: true
60
+ fields:
61
+ - name: id
62
+ type: String
63
+ - name: orderNumber
64
+ type: String
65
+ validations:
66
+ - type: NotBlank
67
+ message: "El número de pedido es obligatorio"
68
+ - name: customerId
69
+ type: String
70
+ reference:
71
+ aggregate: Customer
72
+ module: customers
73
+ - name: status
74
+ type: OrderStatus
75
+ readOnly: true # Manejado por transitions — no va en CreateCommand
76
+ - name: totalAmount
77
+ type: BigDecimal
78
+ readOnly: true # Calculado — no va en CreateCommand
79
+ defaultValue: "0.00"
80
+ - name: notes
81
+ type: String
82
+ relationships:
83
+ # El generador crea automáticamente la relación inversa (ManyToOne) en OrderItem
84
+ # No es necesario declarar la relación inversa en OrderItem
85
+ - type: OneToMany
86
+ target: OrderItem
87
+ mappedBy: order # → OrderItem tendrá un campo 'order' (FK: order_id)
88
+ cascade: [PERSIST, MERGE, REMOVE]
89
+ fetch: LAZY
90
+ # El generador crea automáticamente la relación inversa (OneToOne) en ShippingAddress
91
+ # No es necesario declarar la relación inversa en ShippingAddress
92
+ - type: OneToOne
93
+ target: ShippingAddress
94
+ mappedBy: order # → ShippingAddress tendrá un campo 'order' (FK: order_id)
95
+ cascade: [PERSIST, MERGE, REMOVE]
96
+ fetch: LAZY
97
+
98
+ # ── OneToMany: líneas del pedido ───────────────────────────────────
99
+ - name: OrderItem
100
+ tableName: order_items
101
+ fields:
102
+ - name: id
103
+ type: String
104
+ - name: productId
105
+ type: String
106
+ reference:
107
+ aggregate: Product
108
+ module: catalog
109
+ - name: productName
110
+ type: String
111
+ - name: quantity
112
+ type: Integer
113
+ validations:
114
+ - type: Min
115
+ value: 1
116
+ message: "La cantidad mínima es 1"
117
+ - name: unitPrice
118
+ type: BigDecimal
119
+ - name: subtotal
120
+ type: BigDecimal
121
+ readOnly: true # Calculado por el método de negocio addOrderItem()
122
+ defaultValue: "0.00"
123
+ # ❌ No declarar aquí la relación inversa (ManyToOne hacia Order):
124
+ # el generador la infiere automáticamente desde la definición de Order
125
+
126
+ # ── OneToOne: dirección de envío ───────────────────────────────────
127
+ - name: ShippingAddress
128
+ tableName: order_shipping_addresses
129
+ fields:
130
+ - name: id
131
+ type: String
132
+ - name: street
133
+ type: String
134
+ validations:
135
+ - type: NotBlank
136
+ - name: city
137
+ type: String
138
+ validations:
139
+ - type: NotBlank
140
+ - name: postalCode
141
+ type: String
142
+ - name: country
143
+ type: String
144
+ # ❌ No declarar aquí la relación inversa (OneToOne hacia Order):
145
+ # el generador la infiere automáticamente desde la definición de Order
146
+
147
+ enums:
148
+ - name: OrderStatus
149
+ initialValue: PENDING
150
+ transitions:
151
+ - from: PENDING
152
+ to: CONFIRMED
153
+ method: confirm
154
+ - from: [PENDING, CONFIRMED]
155
+ to: CANCELLED
156
+ method: cancel
157
+ - from: CONFIRMED
158
+ to: SHIPPED
159
+ method: ship
160
+ values: [PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED]
161
+
162
+ events:
163
+ - name: OrderConfirmedEvent
164
+ fields:
165
+ - name: orderId
166
+ type: String
167
+ - name: customerId
168
+ type: String
169
+
170
+
171
+ # ─────────────────────────────────────────────────────────────────────────
172
+ # ENDPOINTS
173
+ # ─────────────────────────────────────────────────────────────────────────
174
+ #
175
+ # Solo controla CUÁLES use cases existen y bajo qué URLs viven.
176
+ # La estructura (campos anidados, listas) la define aggregates: arriba.
177
+
178
+ endpoints:
179
+ basePath: /orders
180
+ versions:
181
+ - version: v1
182
+ operations:
183
+
184
+ # ── CRUD estándar ─────────────────────────────────────────────────
185
+
186
+ - method: POST
187
+ path: /
188
+ description: "Crear pedido con sus líneas y dirección de envío"
189
+ useCase: CreateOrder
190
+ # El Command generado incluirá automáticamente:
191
+ # String orderNumber
192
+ # String customerId
193
+ # String notes
194
+ # List<CreateOrderItemDto> items ← OneToMany
195
+ # CreateShippingAddressDto shippingAddress ← OneToOne
196
+
197
+ - method: GET
198
+ path: /{id}
199
+ description: "Obtener pedido completo con líneas y dirección"
200
+ useCase: GetOrder
201
+ # El ResponseDto tendrá:
202
+ # campos de Order + List<OrderItemDto> + ShippingAddressDto
203
+
204
+ - method: GET
205
+ path: /
206
+ description: "Listar pedidos con paginación"
207
+ useCase: FindAllOrders
208
+
209
+ - method: DELETE
210
+ path: /{id}
211
+ description: "Cancelar y eliminar un pedido"
212
+ useCase: DeleteOrder
213
+
214
+ # ── Operaciones de negocio (scaffold) ────────────────────────────
215
+
216
+ - method: PUT
217
+ path: /{id}/confirm
218
+ description: "Confirmar un pedido pendiente"
219
+ useCase: ConfirmOrder
220
+ # Cat. 1 TRANSITION — nombre coincide con PascalCase(confirm) + Order
221
+ # Genera ConfirmOrderCommand(String id) + handler completo:
222
+ # repository.findById(id) → entity.confirm() → repository.save(entity)
223
+ # type inferido: PUT → command
224
+
225
+ - method: PUT
226
+ path: /{id}/cancel
227
+ description: "Cancelar un pedido"
228
+ useCase: CancelOrder
229
+ # Cat. 1 TRANSITION — cancel + Order
230
+
231
+ - method: PUT
232
+ path: /{id}/ship
233
+ description: "Marcar pedido como enviado"
234
+ useCase: ShipOrder
235
+ # Cat. 1 TRANSITION — ship + Order
236
+
237
+ # ── Operaciones sobre sub-entidades (scaffold) ───────────────────
238
+
239
+ - method: POST
240
+ path: /{id}/items
241
+ description: "Agregar línea a un pedido existente"
242
+ useCase: AddOrderItem
243
+ # Cat. 2a SUB-ENTITY ADD — patrón: Add{target} donde target=OrderItem (OneToMany)
244
+ # Patrón de nombre: Add + rel.target → AddOrderItem
245
+ # Genera AddOrderItemCommand(String id, String productId, String productName, Integer quantity, BigDecimal unitPrice)
246
+ # Handler: findById → entity.addOrderItem(productId, productName, quantity, unitPrice) → save
247
+ # Nota: el campo generado en Order se llama 'orderItems' (auto-pluralizado de OrderItem)
248
+
249
+ - method: DELETE
250
+ path: /{id}/items/{itemId}
251
+ description: "Eliminar una línea del pedido"
252
+ useCase: RemoveOrderItem
253
+ # Cat. 2b SUB-ENTITY REMOVE — patrón: Remove{target} donde target=OrderItem
254
+ # Patrón de nombre: Remove + rel.target → RemoveOrderItem
255
+ # Genera RemoveOrderItemCommand(String id, String itemId)
256
+ # Handler: findById → entity.removeOrderItemById(itemId) → save
257
+
258
+ - method: GET
259
+ path: /customer/{customerId}
260
+ description: "Buscar pedidos por cliente"
261
+ useCase: FindAllOrdersByCustomerId
262
+ # Cat. 3 FIND-BY — patrón FindAll{Aggregate}sBy{FieldPascal}
263
+ # Genera FindAllOrdersByCustomerIdQuery(customerId, page, size, sortBy, sortDirection)
264
+ # Handler: repository.findByCustomerId(customerId, pageable)
265
+ # Además actualiza OrderRepository + OrderJpaRepository + OrderRepositoryImpl
266
+ # type inferido: GET → query
267
+
268
+ # ── SCAFFOLD (nombre libre — ningún patrón lo reconoce) ──────────
269
+
270
+ - method: POST
271
+ path: /{id}/payment
272
+ description: "Procesar el pago de un pedido"
273
+ useCase: ProcessOrderPayment
274
+ # ⚠️ SCAFFOLD — el nombre no coincide con ningún patrón:
275
+ # ✗ No es Create/Get/FindAll/Update/Delete{Aggregate} (estándar)
276
+ # ✗ No es {MethodPascal}{Aggregate} de ninguna transition (Cat. 1)
277
+ # ✗ No es Add{Target} / Remove{Target} de un OneToMany (Cat. 2)
278
+ # ✗ No es FindAll{Aggregate}sBy{Field} (Cat. 3)
279
+ #
280
+ # Por eso genera stubs con TODO y UnsupportedOperationException:
281
+ #
282
+ # ProcessOrderPaymentCommand.java ← record con /* TODO: add fields */
283
+ # ProcessOrderPaymentCommandHandler.java ← lanza UnsupportedOperationException
284
+ #
285
+ # El desarrollador debe:
286
+ # 1. Añadir los campos al Command (paymentMethod, amount, etc.)
287
+ # 2. Implementar la lógica en el Handler
288
+ #
289
+ # type inferido: POST → command (o declarar explícitamente si es GET)
290
+
291
+ # ─────────────────────────────────────────────────────────────────────────
292
+ # RESUMEN DE ARCHIVOS GENERADOS
293
+ # ─────────────────────────────────────────────────────────────────────────
294
+ #
295
+ # domain/models/entities/
296
+ # ├── Order.java (raíz — con métodos confirm/cancel/ship/addOrderItem/removeOrderItemById)
297
+ # ├── OrderItem.java
298
+ # └── ShippingAddress.java
299
+ #
300
+ # domain/models/enums/
301
+ # └── OrderStatus.java
302
+ #
303
+ # domain/models/events/
304
+ # └── OrderConfirmedEvent.java
305
+ #
306
+ # domain/repositories/
307
+ # └── OrderRepository.java ← actualizado con findByCustomerId (Cat.3)
308
+ #
309
+ # infrastructure/database/repositories/
310
+ # ├── OrderJpaRepository.java ← actualizado con findByCustomerId
311
+ # └── OrderRepositoryImpl.java ← actualizado con findByCustomerId
312
+ #
313
+ # application/commands/
314
+ # ├── CreateOrderCommand.java ← ESTÁNDAR — incluye items[] y shippingAddress
315
+ # ├── DeleteOrderCommand.java ← ESTÁNDAR
316
+ # ├── ConfirmOrderCommand.java ← TRANSITION Cat.1 — record(String id)
317
+ # ├── CancelOrderCommand.java ← TRANSITION Cat.1
318
+ # ├── ShipOrderCommand.java ← TRANSITION Cat.1
319
+ # ├── AddOrderItemCommand.java ← SUB-ENTITY ADD Cat.2 — incluye campos de OrderItem
320
+ # ├── RemoveOrderItemCommand.java ← SUB-ENTITY REMOVE Cat.2 — record(id, itemId)
321
+ # └── ProcessOrderPaymentCommand.java ← SCAFFOLD — record con /* TODO: add fields */
322
+ #
323
+ # application/queries/
324
+ # ├── GetOrderQuery.java ← ESTÁNDAR
325
+ # ├── FindAllOrdersQuery.java ← ESTÁNDAR (FindAll + Order + s)
326
+ # └── FindAllOrdersByCustomerIdQuery.java ← FIND-BY Cat.3 — filtra por customerId
327
+ #
328
+ # application/usecases/
329
+ # ├── CreateOrderCommandHandler.java ← ESTÁNDAR — completo
330
+ # ├── DeleteOrderCommandHandler.java ← ESTÁNDAR — completo
331
+ # ├── GetOrderQueryHandler.java ← ESTÁNDAR — completo
332
+ # ├── FindAllOrdersQueryHandler.java ← ESTÁNDAR — paginado completo
333
+ # ├── ConfirmOrderCommandHandler.java ← TRANSITION — findById → entity.confirm() → save
334
+ # ├── CancelOrderCommandHandler.java ← TRANSITION — findById → entity.cancel() → save
335
+ # ├── ShipOrderCommandHandler.java ← TRANSITION — findById → entity.ship() → save
336
+ # ├── AddOrderItemCommandHandler.java ← SUB-ENTITY ADD — findById → entity.addOrderItem(new OrderItem(...)) → save
337
+ # ├── RemoveOrderItemCommandHandler.java ← SUB-ENTITY REMOVE — findById → entity.removeOrderItemById(itemId) → save
338
+ # ├── FindAllOrdersByCustomerIdQueryHandler.java ← FIND-BY — repository.findByCustomerId(customerId, pageable)
339
+ # └── ProcessOrderPaymentCommandHandler.java ← SCAFFOLD — UnsupportedOperationException (TODO)
340
+ #
341
+ # application/dtos/
342
+ # ├── OrderResponseDto.java
343
+ # ├── OrderItemDto.java
344
+ # ├── ShippingAddressDto.java
345
+ # ├── CreateOrderItemDto.java ← para el comando de creación
346
+ # └── CreateShippingAddressDto.java ← para el comando de creación
347
+ #
348
+ # application/mappers/
349
+ # └── OrderApplicationMapper.java
350
+ #
351
+ # infrastructure/rest/controllers/order/
352
+ # └── v1/
353
+ # └── OrderV1Controller.java ← 10 endpoints declarados
@@ -0,0 +1,144 @@
1
+ # EJEMPLO: ENDPOINTS CON VERSIONADO MÚLTIPLE (v1 + v2)
2
+ # Demuestra cómo v1 y v2 coexisten generando controladores independientes.
3
+ #
4
+ # REGLA ANTI-DUPLICADO:
5
+ # Si un useCase declarado en v2 ya fue generado en v1, el generador
6
+ # omite la creación del Command/Query + Handler y SOLO agrega la
7
+ # referencia en el controlador de v2.
8
+ #
9
+ # RESULTADO:
10
+ # - 1 controlador por versión (ProductV1Controller, ProductV2Controller)
11
+ # - Use cases propios de cada versión generados una sola vez
12
+ # - Use cases compartidos referenciados sin regenerar
13
+
14
+ aggregates:
15
+ - name: Product
16
+ entities:
17
+ - name: product
18
+ isRoot: true
19
+ tableName: products
20
+ audit:
21
+ enabled: true
22
+ fields:
23
+ - name: id
24
+ type: String
25
+ - name: name
26
+ type: String
27
+ validations:
28
+ - type: NotBlank
29
+ - name: description
30
+ type: String
31
+ - name: price
32
+ type: BigDecimal
33
+ validations:
34
+ - type: Positive
35
+ - name: stock
36
+ type: Integer
37
+ readOnly: true
38
+ defaultValue: 0
39
+ - name: active
40
+ type: Boolean
41
+ readOnly: true
42
+ defaultValue: true
43
+
44
+ endpoints:
45
+ basePath: /products
46
+ versions:
47
+ # ── v1: CRUD estándar ──────────────────────────────────────────────────
48
+ - version: v1
49
+ operations:
50
+ - method: GET
51
+ path: /{id}
52
+ description: "Obtener producto por ID"
53
+ useCase: FindProductById # completo
54
+
55
+ - method: GET
56
+ path: /
57
+ description: "Listar productos activos"
58
+ useCase: FindAllProducts # completo
59
+
60
+ - method: POST
61
+ path: /
62
+ description: "Crear producto"
63
+ useCase: CreateProduct # completo
64
+
65
+ - method: PUT
66
+ path: /{id}
67
+ description: "Actualizar datos del producto"
68
+ useCase: UpdateProduct # completo
69
+
70
+ - method: DELETE
71
+ path: /{id}
72
+ description: "Eliminar producto"
73
+ useCase: DeleteProduct # completo
74
+
75
+ # ── v2: nuevas operaciones + reuso de use cases de v1 ─────────────────
76
+ - version: v2
77
+ operations:
78
+ - method: GET
79
+ path: /{id}
80
+ description: "Producto con categorías y variantes embebidas"
81
+ useCase: FindProductByIdV2 # NUEVO handler — shape de respuesta distinto
82
+
83
+ - method: GET
84
+ path: /
85
+ description: "Listado con filtros avanzados y metadata extendida"
86
+ useCase: FindAllProductsV2 # NUEVO handler — parámetros de búsqueda distintos
87
+
88
+ - method: POST
89
+ path: /
90
+ description: "Crear producto (misma lógica que v1)"
91
+ useCase: CreateProduct # REUTILIZA v1 — no regenera handler
92
+
93
+ - method: PUT
94
+ path: /{id}
95
+ description: "Actualizar producto (misma lógica que v1)"
96
+ useCase: UpdateProduct # REUTILIZA v1 — no regenera handler
97
+
98
+ - method: DELETE
99
+ path: /{id}
100
+ description: "Eliminar producto (misma lógica que v1)"
101
+ useCase: DeleteProduct # REUTILIZA v1 — no regenera handler
102
+
103
+ - method: PUT
104
+ path: /{id}/activate
105
+ description: "Activar un producto desactivado"
106
+ useCase: ActivateProduct # NUEVO scaffold — TODO: product.activate()
107
+
108
+ - method: PUT
109
+ path: /{id}/deactivate
110
+ description: "Desactivar un producto activo"
111
+ useCase: DeactivateProduct # NUEVO scaffold — TODO: product.deactivate()
112
+
113
+ # Archivos generados:
114
+ #
115
+ # infrastructure/rest/controllers/product/
116
+ # ├── v1/ProductV1Controller.java ← 5 endpoints (v1)
117
+ # └── v2/ProductV2Controller.java ← 7 endpoints (v2)
118
+ #
119
+ # application/commands/ (solo use cases ÚNICOS — sin duplicados)
120
+ # ├── CreateProductCommand.java
121
+ # ├── UpdateProductCommand.java
122
+ # ├── DeleteProductCommand.java
123
+ # ├── ActivateProductCommand.java ← solo v2
124
+ # └── DeactivateProductCommand.java ← solo v2
125
+ #
126
+ # application/queries/ (solo use cases ÚNICOS)
127
+ # ├── FindProductByIdQuery.java
128
+ # ├── FindAllProductsQuery.java
129
+ # ├── FindProductByIdV2Query.java ← solo v2
130
+ # └── FindAllProductsV2Query.java ← solo v2
131
+ #
132
+ # application/usecases/
133
+ # ├── CreateProductCommandHandler.java ← completo
134
+ # ├── UpdateProductCommandHandler.java ← completo
135
+ # ├── DeleteProductCommandHandler.java ← completo
136
+ # ├── ActivateProductCommandHandler.java ← scaffold + TODO
137
+ # ├── DeactivateProductCommandHandler.java ← scaffold + TODO
138
+ # ├── FindProductByIdQueryHandler.java ← completo
139
+ # ├── FindAllProductsQueryHandler.java ← completo
140
+ # ├── FindProductByIdV2QueryHandler.java ← completo (shape distinto)
141
+ # └── FindAllProductsV2QueryHandler.java ← completo (filtros extendidos)
142
+ #
143
+ # CreateProduct, UpdateProduct, DeleteProduct aparecen en v2 pero ya
144
+ # existen de v1 → ProductV2Controller los inyecta sin regenerar handlers.
@@ -0,0 +1,135 @@
1
+ # EJEMPLO: ENDPOINTS DECLARATIVOS — CASO BÁSICO (una versión)
2
+ # Módulo de pedidos con operaciones específicas del negocio.
3
+ #
4
+ # COMPORTAMIENTO CONDICIONAL:
5
+ #
6
+ # SIN sección endpoints: → genera 5 CRUD fijos por defecto:
7
+ # CreateOrder, UpdateOrder, DeleteOrder,
8
+ # FindOrderById, FindAllOrders
9
+ #
10
+ # CON sección endpoints: → genera ÚNICAMENTE las operaciones
11
+ # declaradas en operations[] — ni más, ni menos.
12
+ #
13
+ # TYPE INFERIDO desde method:
14
+ # GET → query
15
+ # POST/PUT/DELETE/PATCH → command
16
+ #
17
+ # SCAFFOLD vs COMPLETO:
18
+ # Use cases estándar (Create*, Update*, Delete*, FindById*, FindAll*):
19
+ # → implementación completa con lógica de repositorio
20
+ # Use cases custom (ConfirmOrder, CancelOrder, etc.):
21
+ # → scaffold con // TODO apuntando al método de negocio sugerido
22
+
23
+ aggregates:
24
+ - name: Order
25
+ entities:
26
+ - name: order
27
+ isRoot: true
28
+ tableName: orders
29
+ audit:
30
+ enabled: true
31
+ trackUser: true
32
+ fields:
33
+ - name: id
34
+ type: String
35
+ - name: orderNumber
36
+ type: String
37
+ validations:
38
+ - type: NotBlank
39
+ message: "Order number is required"
40
+ - name: customerId
41
+ type: String
42
+ reference:
43
+ aggregate: Customer
44
+ module: customers
45
+ - name: status
46
+ type: OrderStatus
47
+ readOnly: true
48
+ - name: totalAmount
49
+ type: BigDecimal
50
+ readOnly: true
51
+ defaultValue: "0.00"
52
+
53
+ enums:
54
+ - name: OrderStatus
55
+ initialValue: PENDING
56
+ transitions:
57
+ - from: PENDING
58
+ to: CONFIRMED
59
+ method: confirm
60
+ - from: [PENDING, CONFIRMED]
61
+ to: CANCELLED
62
+ method: cancel
63
+ values: [PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED]
64
+
65
+ events:
66
+ - name: OrderPlacedEvent
67
+ fields:
68
+ - name: orderId
69
+ type: String
70
+ - name: customerId
71
+ type: String
72
+ kafka: true
73
+
74
+ # ─── Sección endpoints ─────────────────────────────────────────────────────
75
+ # Presente → genera solo los use cases declarados
76
+ # Ausente → comportamiento actual (5 CRUD por defecto)
77
+ # ──────────────────────────────────────────────────────────────────────────
78
+
79
+ endpoints:
80
+ basePath: /orders
81
+ versions:
82
+ - version: v1
83
+ operations:
84
+ - method: GET
85
+ path: /{id}
86
+ description: "Obtener detalle de un pedido"
87
+ useCase: FindOrderById # completo: FindOrderByIdQuery + Handler
88
+
89
+ - method: GET
90
+ path: /
91
+ description: "Listar pedidos con paginación"
92
+ useCase: FindAllOrders # completo: FindAllOrdersQuery + Handler
93
+
94
+ - method: POST
95
+ path: /
96
+ description: "Crear nuevo pedido"
97
+ useCase: CreateOrder # completo: CreateOrderCommand + Handler
98
+
99
+ - method: PUT
100
+ path: /{id}/confirm
101
+ description: "Confirmar pedido pendiente — PENDING → CONFIRMED"
102
+ useCase: ConfirmOrder # scaffold: TODO → order.confirm(); repo.save(order)
103
+
104
+ - method: PUT
105
+ path: /{id}/cancel
106
+ description: "Cancelar pedido — [PENDING, CONFIRMED] → CANCELLED"
107
+ useCase: CancelOrder # scaffold: TODO → order.cancel(); repo.save(order)
108
+
109
+ - method: DELETE
110
+ path: /{id}
111
+ description: "Eliminar pedido"
112
+ useCase: DeleteOrder # completo: DeleteOrderCommand + Handler
113
+
114
+ # Archivos generados con esta configuración:
115
+ #
116
+ # infrastructure/rest/controllers/order/v1/
117
+ # └── OrderV1Controller.java ← 6 métodos exactos
118
+ #
119
+ # application/commands/
120
+ # ├── CreateOrderCommand.java
121
+ # ├── ConfirmOrderCommand.java
122
+ # ├── CancelOrderCommand.java
123
+ # └── DeleteOrderCommand.java
124
+ #
125
+ # application/queries/
126
+ # ├── FindOrderByIdQuery.java
127
+ # └── FindAllOrdersQuery.java
128
+ #
129
+ # application/usecases/
130
+ # ├── CreateOrderCommandHandler.java ← implementación completa
131
+ # ├── ConfirmOrderCommandHandler.java ← scaffold + TODO
132
+ # ├── CancelOrderCommandHandler.java ← scaffold + TODO
133
+ # ├── DeleteOrderCommandHandler.java ← implementación completa
134
+ # ├── FindOrderByIdQueryHandler.java ← implementación completa
135
+ # └── FindAllOrdersQueryHandler.java ← implementación completa