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
@@ -0,0 +1,414 @@
1
+ # EJEMPLO: PORTS — clientes HTTP hacia servicios internos o externos
2
+ # Complemento de listeners: (consumo async), ports: es para comunicación síncrona HTTP.
3
+ #
4
+ # La sección ports: vive en el NIVEL RAÍZ del domain.yaml,
5
+ # como sibling de aggregates: y listeners:.
6
+ #
7
+ # Formato: plano, un método = una entrada (igual que listeners:).
8
+ # Entradas con el mismo service: se agrupan en UN solo cliente Feign.
9
+ #
10
+ # Generación:
11
+ # eva4j g entities reservations
12
+ #
13
+ # ─── PATRÓN ACL (Anti-Corruption Layer) ──────────────────────────────────────
14
+ #
15
+ # Los DTOs de infraestructura (forma exacta de la API externa) viven en:
16
+ # infrastructure/adapters/{service}/{MethodPascal}Dto.java
17
+ #
18
+ # Los modelos de dominio (abstracción interna) viven en:
19
+ # domain/models/{service}/{DomainType}.java
20
+ #
21
+ # La interfaz del puerto devuelve modelos de dominio.
22
+ # El FeignAdapter mapea Dto → modelo de dominio de forma inline.
23
+ # Si la API externa cambia, sólo hay que actualizar el adaptador.
24
+ #
25
+ # El tipo de dominio se deriva automáticamente del nombre del método
26
+ # (ej: findScreeningById → Screening). Se puede sobrescribir con domainType:.
27
+ #
28
+ # ─── ARCHIVOS GENERADOS ──────────────────────────────────────────────────────
29
+ #
30
+ # Por cada service: único:
31
+ # domain/repositories/{ServiceName}.java ← interfaz del puerto
32
+ # infrastructure/adapters/{service}/{ServiceName}FeignClient.java
33
+ # infrastructure/adapters/{service}/{ServiceName}FeignAdapter.java
34
+ # infrastructure/adapters/{service}/{ServiceName}FeignConfig.java
35
+ # parameters/*/urls.yaml ← base-url parametrizada
36
+ #
37
+ # Por cada modelo de dominio único derivado de los métodos con fields:
38
+ # domain/models/{service}/{DomainType}.java ← modelo de dominio (ACL)
39
+ #
40
+ # Por cada método con fields:
41
+ # infrastructure/adapters/{service}/{MethodPascal}Dto.java ← DTO infra (forma externa)
42
+ #
43
+ # Por cada método con body: (POST/PUT/PATCH)
44
+ # application/dtos/{MethodPascal}RequestDto.java
45
+ #
46
+ # Por cada nestedTypes:
47
+ # application/dtos/{NestedTypePascal}.java
48
+ #
49
+ # ─────────────────────────────────────────────────────────────────────────────
50
+ #
51
+ # ARCHIVOS GENERADOS EN ESTE EJEMPLO
52
+ #
53
+ # ScreeningService (módulo interno: screenings)
54
+ # ─────────────────────────────────────────────
55
+ # domain/repositories/ScreeningService.java ← devuelve Screening, Seat, etc.
56
+ # domain/models/screeningService/Screening.java ← modelo de dominio (ACL)
57
+ # domain/models/screeningService/Seat.java ← modelo de dominio (ACL, domainType override)
58
+ # domain/models/screeningService/PrivateEventAvailability.java ← modelo de dominio (ACL)
59
+ # infrastructure/adapters/screeningService/ScreeningServiceFeignClient.java
60
+ # infrastructure/adapters/screeningService/ScreeningServiceFeignAdapter.java
61
+ # infrastructure/adapters/screeningService/ScreeningServiceFeignConfig.java
62
+ # infrastructure/adapters/screeningService/FindScreeningByIdDto.java
63
+ # infrastructure/adapters/screeningService/FindAvailableSeatDto.java
64
+ # infrastructure/adapters/screeningService/CheckPrivateEventAvailabilityDto.java
65
+ #
66
+ # CustomerService (módulo interno: customers)
67
+ # ─────────────────────────────────────────────
68
+ # domain/repositories/CustomerService.java
69
+ # domain/models/customerService/Customer.java ← modelo de dominio (ACL)
70
+ # infrastructure/adapters/customerService/CustomerServiceFeignClient.java
71
+ # infrastructure/adapters/customerService/CustomerServiceFeignAdapter.java
72
+ # infrastructure/adapters/customerService/CustomerServiceFeignConfig.java
73
+ # infrastructure/adapters/customerService/FindCustomerByIdDto.java ← DTO infra
74
+ #
75
+ # PaymentGateway (servicio externo)
76
+ # ─────────────────────────────────────────────
77
+ # domain/repositories/PaymentGateway.java
78
+ # domain/models/paymentGateway/Payment.java ← modelo de dominio (ACL)
79
+ # domain/models/paymentGateway/PaymentStatus.java ← modelo de dominio (ACL)
80
+ # infrastructure/adapters/paymentGateway/PaymentGatewayFeignClient.java
81
+ # infrastructure/adapters/paymentGateway/PaymentGatewayFeignAdapter.java
82
+ # infrastructure/adapters/paymentGateway/PaymentGatewayFeignConfig.java
83
+ # application/dtos/PaymentMethodInput.java ← nestedType del body
84
+ # application/dtos/ProcessPaymentRequestDto.java ← body del POST
85
+ # infrastructure/adapters/paymentGateway/ProcessPaymentDto.java ← response DTO infra
86
+ # infrastructure/adapters/paymentGateway/FindPaymentStatusDto.java ← response DTO infra
87
+ # → cancelPayment: void, sin DTO de respuesta
88
+ #
89
+ # parameters/*/urls.yaml:
90
+ # reservations:
91
+ # screening-service:
92
+ # base-url: http://localhost:8081
93
+ # customer-service:
94
+ # base-url: http://localhost:8082
95
+ # payment-gateway:
96
+ # base-url: https://api.payments.example.com
97
+ #
98
+ # ─────────────────────────────────────────────────────────────────────────────
99
+ #
100
+ # CÓDIGO GENERADO — ScreeningService.java (domain/repositories/)
101
+ # [ACL: retorna modelos de dominio, no DTOs de infraestructura]
102
+ #
103
+ # public interface ScreeningService {
104
+ # Screening findScreeningById(String id);
105
+ # List<Seat> findAvailableSeats(String id);
106
+ # PrivateEventAvailability checkPrivateEventAvailability(String id);
107
+ # }
108
+ #
109
+ # CÓDIGO GENERADO — ScreeningServiceFeignClient.java
110
+ # [FeignClient: devuelve DTOs de infra, no modelos de dominio]
111
+ #
112
+ # @FeignClient(
113
+ # name = "reservations-screening-service",
114
+ # url = "${reservations.screening-service.base-url}",
115
+ # configuration = ScreeningServiceFeignConfig.class
116
+ # )
117
+ # public interface ScreeningServiceFeignClient {
118
+ #
119
+ # @GetMapping("/screenings/{id}")
120
+ # FindScreeningByIdDto findScreeningById(@PathVariable("id") String id);
121
+ #
122
+ # @GetMapping("/screenings/{id}/seats")
123
+ # List<FindAvailableSeatDto> findAvailableSeats(@PathVariable("id") String id);
124
+ #
125
+ # @GetMapping("/screenings/{id}/private-event-availability")
126
+ # CheckPrivateEventAvailabilityDto checkPrivateEventAvailability(
127
+ # @PathVariable("id") String id);
128
+ # }
129
+ #
130
+ # CÓDIGO GENERADO — ScreeningServiceFeignAdapter.java (fragment)
131
+ # [Adapter: actúa como ACL, convierte DTOs infra → modelos de dominio]
132
+ #
133
+ # @Override
134
+ # public Screening findScreeningById(String id) {
135
+ # return toScreening(feignClient.findScreeningById(id));
136
+ # }
137
+ #
138
+ # // ── ACL Mappers ──────────────────────────────────────────────────────────
139
+ # private Screening toScreening(FindScreeningByIdDto dto) {
140
+ # if (dto == null) return null;
141
+ # return new Screening(dto.id(), dto.movieId(), dto.theaterId(), ...);
142
+ # }
143
+ #
144
+ # CÓDIGO GENERADO — PaymentGatewayFeignClient.java
145
+ #
146
+ # @FeignClient(
147
+ # name = "reservations-payment-gateway",
148
+ # url = "${reservations.payment-gateway.base-url}",
149
+ # configuration = PaymentGatewayFeignConfig.class
150
+ # )
151
+ # public interface PaymentGatewayFeignClient {
152
+ #
153
+ # @PostMapping("/payments")
154
+ # ProcessPaymentDto processPayment(@RequestBody ProcessPaymentRequestDto body);
155
+ #
156
+ # @GetMapping("/payments/{id}")
157
+ # FindPaymentStatusDto findPaymentStatus(@PathVariable("id") String id);
158
+ #
159
+ # @DeleteMapping("/payments/{id}")
160
+ # void cancelPayment(@PathVariable("id") String id);
161
+ # }
162
+ #
163
+ # CÓDIGO GENERADO — ProcessPaymentRequestDto.java (application/dtos/)
164
+ #
165
+ # public record ProcessPaymentRequestDto(
166
+ # String reservationId,
167
+ # String customerId,
168
+ # BigDecimal amount,
169
+ # String currency,
170
+ # PaymentMethodInput paymentMethod
171
+ # ) {}
172
+ #
173
+ # CÓDIGO GENERADO — PaymentMethodInput.java (application/dtos/)
174
+ #
175
+ # public record PaymentMethodInput(
176
+ # String type,
177
+ # String cardToken,
178
+ # String cardLastFour
179
+ # ) {}
180
+ #
181
+ # ─── REGLAS ───────────────────────────────────────────────────────────────────
182
+ #
183
+ # domainType: (opcional)
184
+ # • Sobreescribe el nombre del modelo de dominio derivado automáticamente.
185
+ # • Auto-derivación: elimina prefijo de verbo (Find/Get/...) y sufijo By{X}.
186
+ # Ej: findAvailableSeats → Seat (elimina Available + Seats→Seat no, por eso se usa override)
187
+ # • Declarar explícitamente cuando la derivación automática no es adecuada.
188
+ #
189
+ # baseUrl:
190
+ # • Se declara en la PRIMERA entrada de cada service:.
191
+ # • Entradas posteriores del mismo service: la ignoran.
192
+ # • Si se omite en todas las entradas, se usa http://localhost:8080 + warning.
193
+ # • Siempre se escribe en parameters/*/urls.yaml.
194
+ #
195
+ # body: (POST / PUT / PATCH)
196
+ # • Campos de tipo objeto → declarar en nestedTypes:.
197
+ # • En GET o DELETE: emit warning, se ignora.
198
+ # • Omitido → sin @RequestBody en el FeignClient.
199
+ #
200
+ # returnList: true
201
+ # • Solo aplica cuando fields: está presente.
202
+ # • Genera List<{DomainType}> en la interfaz, List<{InfraDto}> en el FeignClient.
203
+ # • Default: false.
204
+ #
205
+ # fields: omitidos → retorno void en la interfaz y en el FeignClient.
206
+ #
207
+ # nestedTypes:
208
+ # • Genera un record auxiliar en application/dtos/ por cada entrada.
209
+ # • Se deduplican globalmente dentro del mismo service:.
210
+ # • Misma sintaxis que en listeners:.
211
+ #
212
+ # ─── CONTRASTE: async vs sync ────────────────────────────────────────────────
213
+ #
214
+ # domain.yaml
215
+ # ├── aggregates:
216
+ # │ └── events: → Domain Events que PRODUCE (async, broker)
217
+ # ├── listeners: → Integration Events que CONSUME (async, broker)
218
+ # └── ports: → Servicios HTTP que LLAMA (sync, Feign)
219
+
220
+ aggregates:
221
+ - name: Reservation
222
+ entities:
223
+ - name: reservation
224
+ isRoot: true
225
+ tableName: reservations
226
+ audit:
227
+ enabled: true
228
+ fields:
229
+ - name: id
230
+ type: String
231
+ - name: screeningId
232
+ type: String
233
+ - name: customerId
234
+ type: String
235
+ - name: status
236
+ type: ReservationStatus
237
+ readOnly: true
238
+ - name: totalAmount
239
+ type: BigDecimal
240
+ readOnly: true
241
+ defaultValue: "0.00"
242
+
243
+ enums:
244
+ - name: ReservationStatus
245
+ initialValue: PENDING
246
+ values: [PENDING, CONFIRMED, CANCELLED, EXPIRED]
247
+ transitions:
248
+ - from: PENDING
249
+ to: CONFIRMED
250
+ method: confirm
251
+ - from: PENDING
252
+ to: CANCELLED
253
+ method: cancel
254
+ - from: PENDING
255
+ to: EXPIRED
256
+ method: expire
257
+
258
+ events:
259
+ - name: ReservationConfirmed
260
+ fields:
261
+ - name: reservationId
262
+ type: String
263
+ - name: confirmedAt
264
+ type: LocalDateTime
265
+
266
+ - name: ReservationCancelled
267
+ fields:
268
+ - name: reservationId
269
+ type: String
270
+ - name: reason
271
+ type: String
272
+
273
+ # ─── Servicios HTTP que este módulo LLAMA ─────────────────────────────────────
274
+ # Nivel raíz, sibling de aggregates: y listeners:
275
+ # baseUrl: se declara en la primera entrada de cada service:.
276
+
277
+ ports:
278
+
279
+ # ── ScreeningService (módulo interno: screenings) ─────────────────────────
280
+
281
+ - name: findScreeningById # nombre del método en la interfaz (camelCase)
282
+ service: ScreeningService # agrupa en un solo FeignClient (PascalCase)
283
+ target: screenings # módulo o servicio destino (referencia documental)
284
+ baseUrl: http://localhost:8081 # base URL → parameters/*/urls.yaml (primera entrada del service)
285
+ http: GET /screenings/{id} # verbo HTTP + path
286
+ fields: # campos de respuesta → FindScreeningByIdResponseDto.java
287
+ - name: id
288
+ type: String
289
+ - name: movieId
290
+ type: String
291
+ - name: theaterId
292
+ type: String
293
+ - name: startTime
294
+ type: LocalDateTime
295
+ - name: endTime
296
+ type: LocalDateTime
297
+ - name: basePrice
298
+ type: BigDecimal
299
+ - name: status
300
+ type: String
301
+ - name: isPrivateEvent
302
+ type: Boolean
303
+
304
+ - name: findAvailableSeats
305
+ service: ScreeningService # misma interfaz y FeignClient que arriba
306
+ target: screenings
307
+ # baseUrl omitida — ya declarada en la primera entrada de este service
308
+ http: GET /screenings/{id}/seats
309
+ returnList: true # → List<Seat> en la interfaz, List<FindAvailableSeatDto> en Feign
310
+ domainType: Seat # sobrescribe la derivación automática (findAvailableSeats → Seat)
311
+ fields:
312
+ - name: seatId
313
+ type: String
314
+ - name: seatRow
315
+ type: String
316
+ - name: seatNumber
317
+ type: Integer
318
+ - name: seatType
319
+ type: String
320
+ - name: availabilityStatus
321
+ type: String
322
+
323
+ - name: checkPrivateEventAvailability
324
+ service: ScreeningService
325
+ target: screenings
326
+ http: GET /screenings/{id}/private-event-availability
327
+ fields:
328
+ - name: screeningId
329
+ type: String
330
+ - name: isAvailable
331
+ type: Boolean
332
+ - name: totalCapacity
333
+ type: Integer
334
+ - name: unavailableSeatsCount
335
+ type: Integer
336
+
337
+ # ── CustomerService (módulo interno: customers) ───────────────────────────
338
+
339
+ - name: findCustomerById
340
+ service: CustomerService
341
+ target: customers
342
+ baseUrl: http://localhost:8082
343
+ http: GET /customers/{id}
344
+ fields:
345
+ - name: id
346
+ type: String
347
+ - name: firstName
348
+ type: String
349
+ - name: lastName
350
+ type: String
351
+ - name: email
352
+ type: String
353
+ - name: status
354
+ type: String
355
+
356
+ # ── PaymentGateway (servicio externo) ─────────────────────────────────────
357
+
358
+ - name: processPayment
359
+ service: PaymentGateway
360
+ target: payment-gateway-external
361
+ baseUrl: https://api.payments.example.com
362
+ http: POST /payments
363
+ body: # @RequestBody → ProcessPaymentRequestDto.java
364
+ - name: reservationId
365
+ type: String
366
+ - name: customerId
367
+ type: String
368
+ - name: amount
369
+ type: BigDecimal
370
+ - name: currency
371
+ type: String
372
+ - name: paymentMethod
373
+ type: PaymentMethodInput # tipo objeto → declarar en nestedTypes:
374
+ nestedTypes: # genera PaymentMethodInput.java en application/dtos/
375
+ - name: paymentMethodInput # camelCase → normalizado a PaymentMethodInput
376
+ fields:
377
+ - name: type
378
+ type: String
379
+ - name: cardToken
380
+ type: String
381
+ - name: cardLastFour
382
+ type: String
383
+ fields: # infra DTO → infrastructure/adapters/paymentGateway/ProcessPaymentDto.java
384
+ - name: paymentId
385
+ type: String
386
+ - name: status
387
+ type: String
388
+ - name: processedAt
389
+ type: LocalDateTime
390
+ - name: transactionCode
391
+ type: String
392
+
393
+ - name: findPaymentStatus
394
+ service: PaymentGateway
395
+ target: payment-gateway-external
396
+ # baseUrl omitida — ya declarada en processPayment (misma entrada de service)
397
+ http: GET /payments/{id}
398
+ domainType: PaymentStatus # override: sin esto, deriveDomainType elimina "Status" y colisiona con Payment
399
+ fields:
400
+ - name: paymentId
401
+ type: String
402
+ - name: status
403
+ type: String
404
+ - name: processedAt
405
+ type: LocalDateTime
406
+ - name: amount
407
+ type: BigDecimal
408
+
409
+ - name: cancelPayment
410
+ service: PaymentGateway
411
+ target: payment-gateway-external
412
+ http: DELETE /payments/{id}
413
+ # body: omitido — sin @RequestBody
414
+ # fields: omitido — retorno void en la interfaz y en el FeignClient
@@ -1,6 +1,6 @@
1
- # EJEMPLO 8: DOMINIO CON SOFT DELETE Y AUDITORÍA AVANZADA
2
- # Testing de entidades con soft delete y auditoría
3
- # Ideal para: Validar características de auditoría y borrado lógico
1
+ # EJEMPLO 8: DOMINIO CON SOFT DELETE Y AUDITORÍA
2
+ # Validar borrado lógico: registros marcados como eliminados no aparecen en queries.
3
+ # Ideal para: sistemas con normativa de retención de datos o restauración de registros.
4
4
 
5
5
  aggregates:
6
6
  - name: Document
@@ -8,7 +8,9 @@ aggregates:
8
8
  - name: Document
9
9
  isRoot: true
10
10
  tableName: documents
11
- auditable: true
11
+ hasSoftDelete: true # ✅ Activa borrado lógico (deletedAt)
12
+ audit:
13
+ enabled: true
12
14
  fields:
13
15
  - name: id
14
16
  type: String
@@ -24,14 +26,6 @@ aggregates:
24
26
  type: Integer
25
27
  - name: authorId
26
28
  type: String
27
- - name: lastModifiedBy
28
- type: String
29
- - name: isDeleted
30
- type: Boolean
31
- - name: deletedAt
32
- type: LocalDateTime
33
- - name: deletedBy
34
- type: String
35
29
  - name: metadata
36
30
  type: DocumentMetadata
37
31
  - name: tags
@@ -42,15 +36,11 @@ aggregates:
42
36
  mappedBy: document
43
37
  cascade: [PERSIST, MERGE]
44
38
  fetch: LAZY
45
- - type: OneToMany
46
- target: Attachment
47
- mappedBy: document
48
- cascade: [PERSIST, MERGE, REMOVE]
49
- fetch: LAZY
50
39
 
51
40
  - name: Revision
52
41
  tableName: document_revisions
53
- auditable: true
42
+ audit:
43
+ enabled: true
54
44
  fields:
55
45
  - name: id
56
46
  type: Long
@@ -64,27 +54,6 @@ aggregates:
64
54
  type: String
65
55
  - name: comment
66
56
  type: String
67
-
68
-
69
- - name: Attachment
70
- tableName: document_attachments
71
- auditable: true
72
- fields:
73
- - name: id
74
- type: Long
75
- - name: fileName
76
- type: String
77
- - name: fileSize
78
- type: Long
79
- - name: mimeType
80
- type: String
81
- - name: storageUrl
82
- type: String
83
- - name: uploadedBy
84
- type: String
85
- - name: isDeleted
86
- type: Boolean
87
-
88
57
 
89
58
  valueObjects:
90
59
  - name: DocumentMetadata
@@ -97,8 +66,6 @@ aggregates:
97
66
  type: String
98
67
  - name: expirationDate
99
68
  type: LocalDate
100
- - name: reviewDate
101
- type: LocalDate
102
69
 
103
70
  enums:
104
71
  - name: DocumentType
@@ -107,16 +74,52 @@ aggregates:
107
74
  - INVOICE
108
75
  - REPORT
109
76
  - PROPOSAL
110
- - MEMO
111
- - POLICY
112
- - PROCEDURE
113
77
  - OTHER
114
78
 
115
79
  - name: DocumentStatus
80
+ initialValue: DRAFT
81
+ transitions:
82
+ - from: DRAFT
83
+ to: IN_REVIEW
84
+ method: submitForReview
85
+ - from: IN_REVIEW
86
+ to: APPROVED
87
+ method: approve
88
+ - from: IN_REVIEW
89
+ to: DRAFT
90
+ method: returnToDraft
91
+ - from: APPROVED
92
+ to: PUBLISHED
93
+ method: publish
116
94
  values:
117
95
  - DRAFT
118
96
  - IN_REVIEW
119
97
  - APPROVED
120
98
  - PUBLISHED
99
+
100
+ # Comportamiento generado por hasSoftDelete: true
101
+ # ─────────────────────────────────────────────────────────────────────────────
102
+ # Entidad de dominio (Document.java):
103
+ # - Campo private LocalDateTime deletedAt; (inyectado automáticamente)
104
+ # - Métodos softDelete() e isDeleted() generados
105
+ # - deletedAt excluido del constructor de creación y de CreateDocumentCommand
106
+ # - deletedAt NO aparece en DocumentResponseDto
107
+ #
108
+ # Entidad JPA (DocumentJpa.java):
109
+ # - @SQLRestriction("deleted_at IS NULL") → Hibernate filtra eliminados en TODAS las queries
110
+ # - Campo @Column(name = "deleted_at") private LocalDateTime deletedAt;
111
+ #
112
+ # Repositorio (DocumentRepository.java):
113
+ # - Sin deleteById() — la eliminación física no está disponible
114
+ #
115
+ # Handler (DeleteDocumentCommandHandler.java):
116
+ # - findById → entity.softDelete() → repository.save(entity)
117
+ # - Nunca llama deleteById()
118
+ #
119
+ # Resultado en API:
120
+ # DELETE /documents/{id} → 200 OK, deletedAt seteado, registro invisible
121
+ # GET /documents/{id} → 404 Not Found (filtrado por @SQLRestriction)
122
+ # GET /documents → no retorna documentos eliminados
123
+
121
124
  - ARCHIVED
122
125
  - OBSOLETE