eva4j 1.0.12 → 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.
@@ -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.
@@ -1253,11 +1364,57 @@ valueObjects:
1253
1364
  type: String
1254
1365
  ```
1255
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
+
1256
1413
  ---
1257
1414
 
1258
1415
  ## Enums
1259
1416
 
1260
- ### Definición
1417
+ ### Definición básica
1261
1418
 
1262
1419
  ```yaml
1263
1420
  enums:
@@ -1314,6 +1471,199 @@ enums:
1314
1471
 
1315
1472
  ---
1316
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
+
1317
1667
  ## Relaciones
1318
1668
 
1319
1669
  eva4j soporta relaciones JPA bidireccionales completas con generación automática del lado inverso.
@@ -2498,22 +2848,26 @@ eva4j generate entities <module-name>
2498
2848
  ### ✅ Soportado
2499
2849
 
2500
2850
  - Agregados con entidad raíz y secundarias
2501
- - Value Objects embebidos
2502
- - Enums con valores
2503
- - 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)
2504
2854
  - Tipos primitivos y de fecha Java
2505
- - Colecciones de primitivos y VOs
2855
+ - Colecciones de primitivos y VOs (`List<T>`)
2506
2856
  - IDs: String (UUID), Long/Integer (IDENTITY)
2507
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`)
2508
2864
 
2509
2865
  ### 🚧 Próximamente
2510
2866
 
2511
- - Validaciones JSR-303
2512
- - Auditoría automática
2513
- - Soft delete
2514
- - Query methods personalizados
2515
- - Índices y constraints
2516
- - 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`)
2517
2871
 
2518
2872
  ---
2519
2873
 
@@ -2529,7 +2883,7 @@ R: Los enums son globales al módulo, solo usa el nombre: `type: OrderStatus`
2529
2883
  R: Sí, pero debes definirlo en cada agregado (por ahora).
2530
2884
 
2531
2885
  **P: ¿Qué pasa si regenero el código?**
2532
- 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.
2533
2887
 
2534
2888
  **P: ¿Puedo personalizar las entidades generadas?**
2535
2889
  R: Sí, modifica las plantillas en `templates/aggregate/`.
@@ -2538,10 +2892,9 @@ R: Sí, modifica las plantillas en `templates/aggregate/`.
2538
2892
 
2539
2893
  ## Recursos Adicionales
2540
2894
 
2541
- - [Guía de Implementación](IMPLEMENTATION_SUMMARY.md)
2542
- - [Guía de Testing](TESTING_GUIDE.md)
2543
2895
  - [Referencia Rápida](QUICK_REFERENCE.md)
2544
- - [Documentación DDD](https://martinfowler.com/bliki/DomainDrivenDesign.html)
2896
+ - [Guía de Agentes IA](AGENTS.md)
2897
+ - [Características Futuras](FUTURE_FEATURES.md)
2545
2898
 
2546
2899
  ---
2547
2900
 
@@ -967,6 +967,98 @@ private Integer age;
967
967
 
968
968
  ---
969
969
 
970
+ ## 16. `defaultValue` para campos `readOnly` (Implementado)
971
+
972
+ Permite especificar un valor inicial para campos `readOnly` directamente en `domain.yaml`. El valor se emite en el **constructor de creación** de la entidad de dominio y como field initializer con `@Builder.Default` en la entidad JPA.
973
+
974
+ ### Sintaxis
975
+
976
+ ```yaml
977
+ entities:
978
+ - name: order
979
+ fields:
980
+ - name: status
981
+ type: OrderStatus
982
+ readOnly: true
983
+ defaultValue: PENDING # Enum value
984
+
985
+ - name: totalAmount
986
+ type: BigDecimal
987
+ readOnly: true
988
+ defaultValue: "0.00" # BigDecimal literal
989
+
990
+ - name: itemCount
991
+ type: Integer
992
+ readOnly: true
993
+ defaultValue: 0 # Integer literal
994
+
995
+ - name: isActive
996
+ type: Boolean
997
+ readOnly: true
998
+ defaultValue: true # Boolean literal
999
+ ```
1000
+
1001
+ ### Código Generado — Dominio
1002
+
1003
+ ```java
1004
+ // Constructor de creación — defaultValue asignado automáticamente
1005
+ public Order(String orderNumber, String customerId) {
1006
+ this.orderNumber = orderNumber;
1007
+ this.customerId = customerId;
1008
+ // readOnly fields initialized with defaultValue:
1009
+ this.status = OrderStatus.PENDING;
1010
+ this.totalAmount = new BigDecimal("0.00");
1011
+ this.itemCount = 0;
1012
+ this.isActive = true;
1013
+ }
1014
+ ```
1015
+
1016
+ ### Código Generado — JPA
1017
+
1018
+ ```java
1019
+ @Builder.Default
1020
+ private OrderStatus status = OrderStatus.PENDING;
1021
+
1022
+ @Builder.Default
1023
+ private BigDecimal totalAmount = new BigDecimal("0.00");
1024
+
1025
+ @Builder.Default
1026
+ private Integer itemCount = 0;
1027
+ ```
1028
+
1029
+ ### Tipos soportados
1030
+
1031
+ | Tipo Java | Ejemplo YAML | Java emitido |
1032
+ |-----------|-------------|---------------|
1033
+ | `String` | `defaultValue: hello` | `"hello"` |
1034
+ | `Integer` / `Long` | `defaultValue: 0` | `0` / `0L` |
1035
+ | `Boolean` | `defaultValue: false` | `false` |
1036
+ | `BigDecimal` | `defaultValue: "0.00"` | `new BigDecimal("0.00")` |
1037
+ | `LocalDateTime` | `defaultValue: now` | `LocalDateTime.now()` |
1038
+ | `LocalDate` | `defaultValue: now` | `LocalDate.now()` |
1039
+ | `Instant` | `defaultValue: now` | `Instant.now()` |
1040
+ | `UUID` | `defaultValue: random` | `UUID.randomUUID()` |
1041
+ | Enum | `defaultValue: ACTIVE` | `EnumType.ACTIVE` |
1042
+
1043
+ ### Reglas
1044
+
1045
+ - `defaultValue` **solo es válido** en campos con `readOnly: true`. Si se usa en un campo no-readOnly, se emite un warning y se ignora.
1046
+ - El campo **sigue siendo readOnly** — no aparece en el constructor de negocio ni en `CreateDto`.
1047
+ - En campos con `autoInit` (enum con `initialValue`), `defaultValue` es ignorado — `autoInit` tiene precedencia.
1048
+
1049
+ ### Archivos Modificados
1050
+
1051
+ | Archivo | Cambio |
1052
+ |---|---|
1053
+ | `src/utils/yaml-to-entity.js` | ✅ `computeJavaDefaultValue()` + `defaultValue` en `parseProperty()` |
1054
+ | `templates/aggregate/AggregateRoot.java.ejs` | ✅ Emite `this.field = defaultValue` en constructor de creación |
1055
+ | `templates/aggregate/DomainEntity.java.ejs` | ✅ Mismo cambio para entidades secundarias |
1056
+ | `templates/aggregate/JpaAggregateRoot.java.ejs` | ✅ `@Builder.Default` + field initializer |
1057
+ | `templates/aggregate/JpaEntity.java.ejs` | ✅ Mismo cambio para entidades JPA secundarias |
1058
+ | `examples/domain-field-visibility.yaml` | ✅ Ejemplos con `defaultValue` en campos readOnly |
1059
+
1060
+ ---
1061
+
970
1062
  ## 15. Transactional Outbox Pattern
971
1063
 
972
1064
  ### Descripción
@@ -1067,9 +1159,10 @@ Domain Events (ítem 1) implementados y funcionando — este ítem solo añade p
1067
1159
  | 13 | Auditoria completa | Impl. | -- | ✅ Implementado |
1068
1160
  | 14 | Validaciones JSR-303 | Impl. | -- | ✅ Implementado |
1069
1161
  | 15 | Transactional Outbox Pattern | Alta | Alta | Pendiente |
1162
+ | 16 | `defaultValue` para campos `readOnly` | Impl. | -- | ✅ Implementado |
1070
1163
 
1071
1164
  ---
1072
1165
 
1073
- **Ultima actualizacion:** 2026-02-25
1166
+ **Ultima actualizacion:** 2026-03-04
1074
1167
  **Version de eva4j:** 1.x
1075
1168
  **Estado:** Documento de planificacion y referencia