eva4j 1.0.11 → 1.0.13

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 (73) hide show
  1. package/AGENTS.md +441 -14
  2. package/DOMAIN_YAML_GUIDE.md +425 -21
  3. package/FUTURE_FEATURES.md +315 -115
  4. package/QUICK_REFERENCE.md +101 -153
  5. package/README.md +77 -70
  6. package/bin/eva4j.js +57 -1
  7. package/config/defaults.json +3 -0
  8. package/docs/commands/GENERATE_ENTITIES.md +662 -1968
  9. package/docs/commands/GENERATE_HTTP_EXCHANGE.md +274 -450
  10. package/docs/commands/GENERATE_KAFKA_EVENT.md +219 -498
  11. package/docs/commands/GENERATE_KAFKA_LISTENER.md +18 -18
  12. package/docs/commands/GENERATE_RECORD.md +335 -311
  13. package/docs/commands/GENERATE_TEMPORAL_ACTIVITY.md +174 -0
  14. package/docs/commands/GENERATE_TEMPORAL_FLOW.md +237 -0
  15. package/docs/commands/GENERATE_USECASE.md +216 -282
  16. package/docs/commands/INDEX.md +36 -7
  17. package/examples/doctor-evaluation.yaml +3 -3
  18. package/examples/domain-audit-complete.yaml +2 -2
  19. package/examples/domain-collections.yaml +2 -2
  20. package/examples/domain-ecommerce.yaml +2 -2
  21. package/examples/domain-events.yaml +201 -0
  22. package/examples/domain-field-visibility.yaml +11 -5
  23. package/examples/domain-multi-aggregate.yaml +12 -6
  24. package/examples/domain-one-to-many.yaml +1 -1
  25. package/examples/domain-one-to-one.yaml +1 -1
  26. package/examples/domain-secondary-onetomany.yaml +1 -1
  27. package/examples/domain-secondary-onetoone.yaml +1 -1
  28. package/examples/domain-simple.yaml +1 -1
  29. package/examples/domain-soft-delete.yaml +3 -3
  30. package/examples/domain-transitions.yaml +1 -1
  31. package/examples/domain-value-objects.yaml +1 -1
  32. package/package.json +2 -2
  33. package/src/commands/add-kafka-client.js +3 -1
  34. package/src/commands/add-temporal-client.js +286 -0
  35. package/src/commands/generate-entities.js +75 -4
  36. package/src/commands/generate-kafka-event.js +273 -89
  37. package/src/commands/generate-temporal-activity.js +228 -0
  38. package/src/commands/generate-temporal-flow.js +216 -0
  39. package/src/generators/module-generator.js +1 -0
  40. package/src/generators/shared-generator.js +26 -0
  41. package/src/utils/yaml-to-entity.js +93 -4
  42. package/templates/aggregate/AggregateRepository.java.ejs +3 -2
  43. package/templates/aggregate/AggregateRepositoryImpl.java.ejs +15 -7
  44. package/templates/aggregate/AggregateRoot.java.ejs +38 -2
  45. package/templates/aggregate/DomainEntity.java.ejs +6 -2
  46. package/templates/aggregate/DomainEventHandler.java.ejs +62 -0
  47. package/templates/aggregate/DomainEventRecord.java.ejs +50 -0
  48. package/templates/aggregate/JpaAggregateRoot.java.ejs +3 -1
  49. package/templates/aggregate/JpaEntity.java.ejs +3 -1
  50. package/templates/base/docker/kafka-services.yaml.ejs +2 -2
  51. package/templates/base/docker/temporal-services.yaml.ejs +29 -0
  52. package/templates/base/resources/parameters/develop/temporal.yaml.ejs +9 -0
  53. package/templates/base/resources/parameters/local/temporal.yaml.ejs +9 -0
  54. package/templates/base/resources/parameters/production/temporal.yaml.ejs +9 -0
  55. package/templates/base/resources/parameters/test/temporal.yaml.ejs +9 -0
  56. package/templates/base/root/AGENTS.md.ejs +916 -51
  57. package/templates/crud/Controller.java.ejs +36 -6
  58. package/templates/crud/ListQuery.java.ejs +6 -2
  59. package/templates/crud/ListQueryHandler.java.ejs +24 -10
  60. package/templates/crud/UpdateCommand.java.ejs +52 -0
  61. package/templates/crud/UpdateCommandHandler.java.ejs +105 -0
  62. package/templates/kafka-event/DomainEventHandlerMethod.ejs +1 -0
  63. package/templates/kafka-event/Event.java.ejs +23 -0
  64. package/templates/shared/application/dtos/PagedResponse.java.ejs +30 -0
  65. package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +104 -0
  66. package/templates/shared/domain/DomainEvent.java.ejs +40 -0
  67. package/templates/shared/interfaces/HeavyActivity.java.ejs +4 -0
  68. package/templates/shared/interfaces/LightActivity.java.ejs +4 -0
  69. package/templates/temporal-activity/ActivityImpl.java.ejs +14 -0
  70. package/templates/temporal-activity/ActivityInterface.java.ejs +11 -0
  71. package/templates/temporal-flow/WorkFlowImpl.java.ejs +64 -0
  72. package/templates/temporal-flow/WorkFlowInterface.java.ejs +19 -0
  73. package/templates/temporal-flow/WorkFlowService.java.ejs +49 -0
@@ -49,9 +49,9 @@ Las entidades de dominio generadas siguen estrictamente los principios de Domain
49
49
  - ✅ Protección de invariantes del dominio
50
50
 
51
51
  **✅ Constructores sin Validaciones Automáticas:**
52
- - Los constructores asignan valores directamente sin validaciones automáticas
53
- - Las validaciones se implementarán en un release futuro mediante configuración en domain.yaml
54
- - Por ahora, las validaciones deben implementarse manualmente en métodos de negocio según sea necesario
52
+ - Los constructores asignan valores directamente sin validaciones de Bean Validation
53
+ - Las validaciones JSR-303 se declaran en `domain.yaml` y se aplican en la capa de aplicación (Command y CreateDto), no en el dominio
54
+ - Las reglas de invariantes de dominio deben implementarse manualmente en los métodos de negocio
55
55
 
56
56
  **📦 Inmutabilidad de Value Objects:**
57
57
  - Campos declarados como `final`
@@ -132,17 +132,28 @@ aggregates:
132
132
  # Enumeraciones del dominio
133
133
  - name: EnumName
134
134
  values: []
135
+
136
+ events:
137
+ # Eventos de dominio que emite este agregado
138
+ - name: NombreEventoOcurrido
139
+ fields: []
140
+ # kafka: true # opcional — genera publicación a Kafka vía MessageBroker
135
141
  ```
136
142
 
137
143
  ### Ubicación del archivo
138
144
 
139
145
  ```
140
146
  tu-proyecto/
141
- └── modules/
142
- └── tu-modulo/
143
- └── domain.yaml ← Aquí
147
+ └── src/
148
+ └── main/
149
+ └── java/
150
+ └── com/example/myapp/ ← packagePath (ej: com.example.myapp)
151
+ └── orders/ ← moduleName
152
+ └── domain.yaml ← Aquí
144
153
  ```
145
154
 
155
+ Eva4j espera el `domain.yaml` dentro de la carpeta del módulo, que se encuentra bajo la ruta del package Java. Esta ubicación es creada automáticamente al ejecutar `eva add module <nombre>`.
156
+
146
157
  ---
147
158
 
148
159
  ## Definición de Agregados
@@ -257,6 +268,13 @@ fields:
257
268
  **Propiedades soportadas:**
258
269
  - `name`: Nombre del campo (obligatorio)
259
270
  - `type`: Tipo de dato Java (obligatorio)
271
+ - `readOnly`: Excluye del constructor de negocio y CreateDto (ver [Control de Visibilidad](#control-de-visibilidad-de-campos))
272
+ - `hidden`: Excluye del ResponseDto
273
+ - `defaultValue`: Valor inicial para campos `readOnly` (ver [Ó `defaultValue`](#-defaultvalue-valor-inicial-para-campos-readonly))
274
+ - `validations`: Anotaciones JSR-303 en Command y CreateDto
275
+ - `reference`: Referencia semántica a otro agregado
276
+ - `annotations`: Anotaciones JPA personalizadas
277
+ - `isValueObject` / `isEmbedded`: Marcas explícitas de Value Object
260
278
 
261
279
  #### Detección automática de tipos
262
280
 
@@ -407,6 +425,7 @@ Eva4j permite controlar qué campos participan en constructores, DTOs de creaci
407
425
  |-------|---------------------|----------------------|-----------|-------------|
408
426
  | **Normal** | ✅ Incluido | ✅ Incluido | ✅ Incluido | ✅ Incluido |
409
427
  | **`readOnly: true`** | ❌ Excluido | ✅ Incluido | ❌ Excluido | ✅ Incluido |
428
+ | **`readOnly` + `defaultValue`** | ⚡ Asignado con default | ✅ Incluido | ❌ Excluido | ✅ Incluido |
410
429
  | **`hidden: true`** | ✅ Incluido | ✅ Incluido | ✅ Incluido | ❌ Excluido |
411
430
  | **Ambos flags** | ❌ Excluido | ✅ Incluido | ❌ Excluido | ❌ Excluido |
412
431
 
@@ -488,6 +507,98 @@ public record OrderResponseDto(
488
507
  ) {}
489
508
  ```
490
509
 
510
+ #### 🎯 `defaultValue` - Valor Inicial para campos `readOnly`
511
+
512
+ Permite asignar un valor inicial predecible a un campo `readOnly` directamente en `domain.yaml`. El generador emite la asignación en el **constructor de creación** de la entidad de dominio y añade `@Builder.Default` en la entidad JPA.
513
+
514
+ **Restricción:** Solo aplica a campos `readOnly: true`. Si se usa sin `readOnly`, eva4j emite un warning y lo ignora.
515
+
516
+ **Casos de uso típicos:**
517
+ - Estado inicial de enums (`PENDING`, `ACTIVE`, `DRAFT`)
518
+ - Contadores que comienzan en cero
519
+ - Totales/acumuladores antes de cálculos
520
+ - Flags booleanos con estado conocido al crear
521
+
522
+ **Sintaxis:**
523
+ ```yaml
524
+ fields:
525
+ - name: totalAmount
526
+ type: BigDecimal
527
+ readOnly: true
528
+ defaultValue: "0.00" # ✅ BigDecimal literal
529
+
530
+ - name: itemCount
531
+ type: Integer
532
+ readOnly: true
533
+ defaultValue: 0 # ✅ Integer literal
534
+
535
+ - name: status
536
+ type: OrderStatus
537
+ readOnly: true
538
+ defaultValue: PENDING # ✅ Enum value (sin comillas)
539
+
540
+ - name: isActive
541
+ type: Boolean
542
+ readOnly: true
543
+ defaultValue: true # ✅ Boolean literal
544
+ ```
545
+
546
+ **Código generado en la entidad de dominio:**
547
+ ```java
548
+ // Constructor de creación — defaultValues asignados automáticamente
549
+ public Order(String orderNumber, String customerId) {
550
+ this.orderNumber = orderNumber;
551
+ this.customerId = customerId;
552
+ this.totalAmount = new BigDecimal("0.00"); // ← defaultValue
553
+ this.itemCount = 0; // ← defaultValue
554
+ this.status = OrderStatus.PENDING; // ← defaultValue
555
+ this.isActive = true; // ← defaultValue
556
+ }
557
+
558
+ // Constructor completo (reconstrucción desde BD) — sin defaultValues
559
+ public Order(String id, String orderNumber, String customerId,
560
+ BigDecimal totalAmount, Integer itemCount,
561
+ OrderStatus status, Boolean isActive, ...) {
562
+ // Todos los campos asignados desde parámetros
563
+ }
564
+ ```
565
+
566
+ **Código generado en la entidad JPA:**
567
+ ```java
568
+ @Builder.Default
569
+ private BigDecimal totalAmount = new BigDecimal("0.00");
570
+
571
+ @Builder.Default
572
+ private Integer itemCount = 0;
573
+
574
+ @Enumerated(EnumType.STRING)
575
+ @Builder.Default
576
+ private OrderStatus status = OrderStatus.PENDING;
577
+ ```
578
+
579
+ **Tipos Java soportados y su forma de emisión:**
580
+
581
+ | Tipo | Ejemplo YAML | Literal Java emitido |
582
+ |------|-------------|----------------------|
583
+ | `String` | `defaultValue: hello` | `"hello"` |
584
+ | `Integer` | `defaultValue: 0` | `0` |
585
+ | `Long` | `defaultValue: 0` | `0L` |
586
+ | `Boolean` | `defaultValue: false` | `false` |
587
+ | `BigDecimal` | `defaultValue: "0.00"` | `new BigDecimal("0.00")` |
588
+ | `LocalDateTime` | `defaultValue: now` | `LocalDateTime.now()` |
589
+ | `LocalDate` | `defaultValue: now` | `LocalDate.now()` |
590
+ | `Instant` | `defaultValue: now` | `Instant.now()` |
591
+ | `UUID` | `defaultValue: random` | `UUID.randomUUID()` |
592
+ | Enum | `defaultValue: ACTIVE` | `EnumType.ACTIVE` |
593
+
594
+ **Compatibilidad con otros flags:**
595
+
596
+ | Combinación | Constructor Creación | JPA Builder | CreateDto | ResponseDto |
597
+ |------------|:-------------------:|:-----------:|:---------:|:-----------:|
598
+ | `readOnly` + `defaultValue` | ⚡ Asignado con default | `@Builder.Default` | ❌ | ✅ |
599
+ | `readOnly` + `hidden` + `defaultValue` | ⚡ Asignado con default | `@Builder.Default` | ❌ | ❌ |
600
+ | `defaultValue` sin `readOnly` | *Ignorado (warning)* | *Ignorado* | — | — |
601
+
491
602
  #### 🙈 `hidden: true` - Campos Sensibles/Internos
492
603
 
493
604
  Marca campos que **NO deben exponerse** en respuestas de API pero sí pueden recibirse en creación.
@@ -683,6 +794,57 @@ fields:
683
794
 
684
795
  ---
685
796
 
797
+ ### Referencias entre Agregados (`reference:`)
798
+
799
+ La propiedad `reference:` declara explícitamente que un campo es un puntero intencional a la raíz de otro agregado. El campo sigue siendo un tipo primitivo (`String`, `Long`, etc.) — **no se genera ningún `@ManyToOne`**.
800
+
801
+ #### Sintaxis
802
+
803
+ ```yaml
804
+ fields:
805
+ - name: customerId
806
+ type: String
807
+ reference:
808
+ aggregate: Customer # Nombre del agregado referenciado (PascalCase) — obligatorio
809
+ module: customers # Módulo donde vive el agregado — opcional
810
+ - name: productId
811
+ type: String
812
+ reference:
813
+ aggregate: Product
814
+ module: catalog
815
+ ```
816
+
817
+ #### Comportamiento
818
+
819
+ - El tipo Java **no cambia** — sigue siendo `String`, `Long`, UUID, etc.
820
+ - JPA genera `@Column` normal — **sin** `@ManyToOne` ni `@JoinColumn`.
821
+ - En la entidad de dominio y en la entidad JPA se genera un **comentario Javadoc** que documenta la referencia.
822
+ - `module:` es opcional: puede omitirse si el agregado referenciado está en el mismo módulo.
823
+ - Si `reference:` está malformado (falta `aggregate`), eva4j lanza un error descriptivo.
824
+
825
+ #### Código Generado
826
+
827
+ ```java
828
+ // domain/models/entities/Order.java
829
+ /** Cross-aggregate reference → Customer (module: customers) */
830
+ private String customerId;
831
+ ```
832
+
833
+ ```java
834
+ // infrastructure/database/entities/OrderJpa.java
835
+ @Column(name = "customer_id")
836
+ /** Cross-aggregate reference → Customer (module: customers) */
837
+ private String customerId;
838
+ ```
839
+
840
+ #### Por qué no usar `@ManyToOne` entre agregados
841
+
842
+ En DDD cada agregado es una unidad transaccional independiente. Un `@ManyToOne` cruzando límites crea un único grafo JPA que viola los límites transaccionales y crea dependencias de carga invisibles. La referencia por ID es el patrón correcto: el handler que necesite los datos del otro agregado los obtiene explícitamente via su propio repositorio.
843
+
844
+ - **Ejemplo completo:** [examples/domain-multi-aggregate.yaml](../examples/domain-multi-aggregate.yaml)
845
+
846
+ ---
847
+
686
848
  ### Validaciones JSR-303
687
849
 
688
850
  Eva4j soporta anotaciones Bean Validation (JSR-303/Jakarta Validation) en campos del `domain.yaml`. Las validaciones se generan **únicamente en la capa de aplicación**: en el `Create<Aggregate>Command` y en los `Create<Entity>Dto` de entidades secundarias. **No se aplican a entidades de dominio** ni a campos con `readOnly: true`.
@@ -1202,11 +1364,57 @@ valueObjects:
1202
1364
  type: String
1203
1365
  ```
1204
1366
 
1367
+ ### Value Objects con métodos de negocio
1368
+
1369
+ Los Value Objects pueden declarar métodos de negocio directamente en el `domain.yaml`. Estos se generan como métodos públicos en la clase del Value Object.
1370
+
1371
+ ```yaml
1372
+ valueObjects:
1373
+ - name: Money
1374
+ fields:
1375
+ - name: amount
1376
+ type: BigDecimal
1377
+ - name: currency
1378
+ type: String
1379
+ methods:
1380
+ - name: add
1381
+ returnType: Money
1382
+ parameters:
1383
+ - name: other
1384
+ type: Money
1385
+ body: "return new Money(this.amount.add(other.getAmount()), this.currency);"
1386
+
1387
+ - name: isPositive
1388
+ returnType: boolean
1389
+ parameters: []
1390
+ body: "return this.amount.compareTo(BigDecimal.ZERO) > 0;"
1391
+ ```
1392
+
1393
+ **Código generado:**
1394
+ ```java
1395
+ public Money add(Money other) {
1396
+ return new Money(this.amount.add(other.getAmount()), this.currency);
1397
+ }
1398
+
1399
+ public boolean isPositive() {
1400
+ return this.amount.compareTo(BigDecimal.ZERO) > 0;
1401
+ }
1402
+ ```
1403
+
1404
+ **Propiedades de un método:**
1405
+
1406
+ | Propiedad | Descripción |
1407
+ |-----------|-------------|
1408
+ | `name` | Nombre del método |
1409
+ | `returnType` | Tipo de retorno Java |
1410
+ | `parameters` | Array de `{ name, type }` |
1411
+ | `body` | Cuerpo del método (string Java) |
1412
+
1205
1413
  ---
1206
1414
 
1207
1415
  ## Enums
1208
1416
 
1209
- ### Definición
1417
+ ### Definición básica
1210
1418
 
1211
1419
  ```yaml
1212
1420
  enums:
@@ -1263,6 +1471,199 @@ enums:
1263
1471
 
1264
1472
  ---
1265
1473
 
1474
+ ### Enums con Transiciones de Estado
1475
+
1476
+ Cuando un enum representa un ciclo de vida de negocio, puede declarar `transitions` e `initialValue`. Eva4j genera automáticamente los métodos de transición, guards, helpers de consulta y el mapa de transiciones válidas.
1477
+
1478
+ #### Sintaxis
1479
+
1480
+ ```yaml
1481
+ enums:
1482
+ - name: OrderStatus
1483
+ initialValue: PENDING # ← Estado inicial (se asigna en el constructor de creación)
1484
+ transitions:
1485
+ - from: PENDING
1486
+ to: CONFIRMED
1487
+ method: confirm
1488
+
1489
+ - from: CONFIRMED
1490
+ to: SHIPPED
1491
+ method: ship
1492
+
1493
+ - from: SHIPPED
1494
+ to: DELIVERED
1495
+ method: deliver
1496
+
1497
+ - from: [PENDING, CONFIRMED] # múltiples estados origen
1498
+ to: CANCELLED
1499
+ method: cancel
1500
+ guard: "this.status == OrderStatus.DELIVERED" # lanza BusinessException si se cumple
1501
+
1502
+ values:
1503
+ - PENDING
1504
+ - CONFIRMED
1505
+ - SHIPPED
1506
+ - DELIVERED
1507
+ - CANCELLED
1508
+ ```
1509
+
1510
+ #### Propiedades de transición
1511
+
1512
+ | Propiedad | Tipo | Descripción |
1513
+ |-----------|------|-------------|
1514
+ | `from` | String \| Array | Estado(s) de origen válidos |
1515
+ | `to` | String | Estado destino |
1516
+ | `method` | String | Nombre del método que ejecuta la transición |
1517
+ | `guard` | String | Condición Java que lanza `BusinessException` si se cumple (opcional) |
1518
+
1519
+ #### Qué genera
1520
+
1521
+ **En el enum (`OrderStatus.java`):**
1522
+ ```java
1523
+ public enum OrderStatus {
1524
+ PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED;
1525
+
1526
+ private static final Map<OrderStatus, Set<OrderStatus>> VALID_TRANSITIONS;
1527
+ // ... mapa estático inicializado
1528
+
1529
+ public boolean canTransitionTo(OrderStatus target) { ... }
1530
+ public OrderStatus transitionTo(OrderStatus target) { ... } // lanza InvalidStateTransitionException
1531
+ }
1532
+ ```
1533
+
1534
+ **En la entidad raíz (`Order.java`):**
1535
+ ```java
1536
+ // El constructor de creación NO recibe status (se auto-inicializa a PENDING)
1537
+ public Order(String orderNumber, String customerId) {
1538
+ this.orderNumber = orderNumber;
1539
+ this.customerId = customerId;
1540
+ this.status = OrderStatus.PENDING; // ← initialValue aplicado
1541
+ }
1542
+
1543
+ // Métodos de transición generados
1544
+ public void confirm() { this.status = this.status.transitionTo(OrderStatus.CONFIRMED); }
1545
+ public void ship() { this.status = this.status.transitionTo(OrderStatus.SHIPPED); }
1546
+ public void deliver() { this.status = this.status.transitionTo(OrderStatus.DELIVERED); }
1547
+ public void cancel() {
1548
+ if (this.status == OrderStatus.DELIVERED) {
1549
+ throw new BusinessException("Cannot execute 'cancel': business rule violated");
1550
+ }
1551
+ this.status = this.status.transitionTo(OrderStatus.CANCELLED);
1552
+ }
1553
+
1554
+ // Helpers de consulta de estado
1555
+ public boolean isPending() { return this.status == OrderStatus.PENDING; }
1556
+ public boolean isConfirmed() { return this.status == OrderStatus.CONFIRMED; }
1557
+ // ... uno por cada valor del enum
1558
+
1559
+ // Helpers de disponibilidad de transición
1560
+ public boolean canConfirm() { return this.status.canTransitionTo(OrderStatus.CONFIRMED); }
1561
+ public boolean canCancel() { return this.status.canTransitionTo(OrderStatus.CANCELLED); }
1562
+ // ... uno por cada método de transición
1563
+ ```
1564
+
1565
+ **Nota:** El campo con `initialValue` se trata implícitamente como `readOnly: true` — no aparece en el constructor de negocio ni en el `CreateDto`.
1566
+
1567
+ ---
1568
+
1569
+ ## Eventos de Dominio
1570
+
1571
+ Los eventos de dominio representan hechos significativos que ocurren dentro del agregado. Eva4j genera las clases de evento y el handler para publicarlos automáticamente tras commit de transacción.
1572
+
1573
+ ### Sintaxis
1574
+
1575
+ ```yaml
1576
+ aggregates:
1577
+ - name: Order
1578
+ entities:
1579
+ - name: order
1580
+ isRoot: true
1581
+ tableName: orders
1582
+ fields:
1583
+ - name: id
1584
+ type: String
1585
+ - name: orderNumber
1586
+ type: String
1587
+
1588
+ events:
1589
+ - name: OrderConfirmedEvent # PascalCase, idealmente en pasado
1590
+ fields:
1591
+ - name: orderId
1592
+ type: String
1593
+ - name: confirmedAt
1594
+ type: LocalDateTime
1595
+
1596
+ - name: OrderShippedEvent
1597
+ kafka: true # opcional — genera publicación a Kafka
1598
+ fields:
1599
+ - name: orderId
1600
+ type: String
1601
+ - name: trackingNumber
1602
+ type: String
1603
+ ```
1604
+
1605
+ ### Propiedades de un evento
1606
+
1607
+ | Propiedad | Tipo | Descripción |
1608
+ |-----------|------|-------------|
1609
+ | `name` | String | Nombre de la clase del evento (PascalCase) |
1610
+ | `fields` | Array | Campos que transporta el evento |
1611
+ | `kafka` | Boolean | Si `true`, genera llamada a `messageBroker.send{EventName}()` |
1612
+
1613
+ ### Archivos generados
1614
+
1615
+ Para cada evento, eva4j genera dos archivos:
1616
+
1617
+ **1. `OrderConfirmedEvent.java`** — en `domain/models/events/`
1618
+ ```java
1619
+ public final class OrderConfirmedEvent extends DomainEvent {
1620
+ private final String orderId;
1621
+ private final LocalDateTime confirmedAt;
1622
+
1623
+ public OrderConfirmedEvent(String aggregateId, String orderId, LocalDateTime confirmedAt) {
1624
+ super(aggregateId);
1625
+ this.orderId = orderId;
1626
+ this.confirmedAt = confirmedAt;
1627
+ }
1628
+
1629
+ public String getOrderId() { return orderId; }
1630
+ public LocalDateTime getConfirmedAt() { return confirmedAt; }
1631
+ }
1632
+ ```
1633
+
1634
+ **2. `OrderDomainEventHandler.java`** — en `application/usecases/`
1635
+ ```java
1636
+ @Component
1637
+ public class OrderDomainEventHandler {
1638
+
1639
+ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
1640
+ public void handle(OrderConfirmedEvent event) {
1641
+ // Lógica post-commit — ej: notificaciones, métricas
1642
+ }
1643
+
1644
+ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
1645
+ public void handle(OrderShippedEvent event) {
1646
+ messageBroker.sendOrderShippedEvent(event); // kafka: true
1647
+ }
1648
+ }
1649
+ ```
1650
+
1651
+ ### Cómo publicar un evento
1652
+
1653
+ Los eventos se publican desde métodos de negocio de la entidad raíz usando el método heredado `raise()`:
1654
+
1655
+ ```java
1656
+ // En Order.java (entidad de dominio)
1657
+ public void confirm() {
1658
+ this.status = this.status.transitionTo(OrderStatus.CONFIRMED);
1659
+ raise(new OrderConfirmedEvent(this.id, this.id, LocalDateTime.now()));
1660
+ }
1661
+ ```
1662
+
1663
+ > **Nota:** `raise()` es provisto por la clase base de dominio. La publicación real ocurre tras el commit de la transacción gracias a `@TransactionalEventListener(AFTER_COMMIT)`.
1664
+
1665
+ ---
1666
+
1266
1667
  ## Relaciones
1267
1668
 
1268
1669
  eva4j soporta relaciones JPA bidireccionales completas con generación automática del lado inverso.
@@ -2447,22 +2848,26 @@ eva4j generate entities <module-name>
2447
2848
  ### ✅ Soportado
2448
2849
 
2449
2850
  - Agregados con entidad raíz y secundarias
2450
- - Value Objects embebidos
2451
- - Enums con valores
2452
- - Relaciones OneToMany, ManyToOne, OneToOne
2851
+ - Value Objects embebidos (con `methods` opcionales)
2852
+ - Enums simples y con transiciones de estado (`transitions`, `initialValue`)
2853
+ - Relaciones OneToMany, ManyToOne, OneToOne (bidireccionales automáticas)
2453
2854
  - Tipos primitivos y de fecha Java
2454
- - Colecciones de primitivos y VOs
2855
+ - Colecciones de primitivos y VOs (`List<T>`)
2455
2856
  - IDs: String (UUID), Long/Integer (IDENTITY)
2456
2857
  - Cascade y Fetch personalizados
2858
+ - Validaciones JSR-303 en Command y CreateDto
2859
+ - Auditoría automática (`audit.enabled`, `audit.trackUser`)
2860
+ - Control de visibilidad de campos (`readOnly`, `hidden`, `defaultValue`)
2861
+ - Referencias cross-agregado (`reference:`)
2862
+ - Domain Events (`events:` con soporte opcional de Kafka)
2863
+ - Soft delete a nivel de módulo (configurado en `eva add module`)
2457
2864
 
2458
2865
  ### 🚧 Próximamente
2459
2866
 
2460
- - Validaciones JSR-303
2461
- - Auditoría automática
2462
- - Soft delete
2463
- - Query methods personalizados
2464
- - Índices y constraints
2465
- - Herencia de entidades
2867
+ - Query methods personalizados en repositorios
2868
+ - Índices y constraints de BD declarados en YAML
2869
+ - Herencia de entidades JPA
2870
+ - Soporte de `Instant` como tipo de campo (actualmente solo para `defaultValue`)
2466
2871
 
2467
2872
  ---
2468
2873
 
@@ -2478,7 +2883,7 @@ R: Los enums son globales al módulo, solo usa el nombre: `type: OrderStatus`
2478
2883
  R: Sí, pero debes definirlo en cada agregado (por ahora).
2479
2884
 
2480
2885
  **P: ¿Qué pasa si regenero el código?**
2481
- R: Se sobrescriben los archivos. Modifica solo en templates, no en código generado.
2886
+ R: Eva4j usa checksums SHA-256 para detectar archivos que fueron modificados manualmente. Los archivos con cambios manuales **no se sobreescriben** — se muestra un aviso y se omiten. Usa `--force` para forzar la sobreescritura de todos los archivos.
2482
2887
 
2483
2888
  **P: ¿Puedo personalizar las entidades generadas?**
2484
2889
  R: Sí, modifica las plantillas en `templates/aggregate/`.
@@ -2487,10 +2892,9 @@ R: Sí, modifica las plantillas en `templates/aggregate/`.
2487
2892
 
2488
2893
  ## Recursos Adicionales
2489
2894
 
2490
- - [Guía de Implementación](IMPLEMENTATION_SUMMARY.md)
2491
- - [Guía de Testing](TESTING_GUIDE.md)
2492
2895
  - [Referencia Rápida](QUICK_REFERENCE.md)
2493
- - [Documentación DDD](https://martinfowler.com/bliki/DomainDrivenDesign.html)
2896
+ - [Guía de Agentes IA](AGENTS.md)
2897
+ - [Características Futuras](FUTURE_FEATURES.md)
2494
2898
 
2495
2899
  ---
2496
2900