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
@@ -0,0 +1,201 @@
1
+ # EJEMPLO: DOMAIN EVENTS
2
+ # Muestra cómo declarar eventos de dominio en domain.yaml
3
+ # Los eventos son siblings de entities:, valueObjects: y enums:
4
+ #
5
+ # Generación:
6
+ # eva4j g entities orders
7
+ #
8
+ # ─── ARCHIVOS GENERADOS ───────────────────────────────────────────────────────
9
+ #
10
+ # 1. shared/domain/DomainEvent.java (una sola vez por proyecto)
11
+ #
12
+ # public abstract class DomainEvent {
13
+ # private final String eventId; // UUID autogenerado
14
+ # private final LocalDateTime occurredOn;
15
+ # private final String aggregateId;
16
+ #
17
+ # protected DomainEvent(String aggregateId) {
18
+ # this.eventId = UUID.randomUUID().toString();
19
+ # this.occurredOn = LocalDateTime.now();
20
+ # this.aggregateId = aggregateId;
21
+ # }
22
+ # // getters...
23
+ # }
24
+ #
25
+ # ─────────────────────────────────────────────────────────────────────────────
26
+ #
27
+ # 2. orders/domain/models/events/OrderPlacedEvent.java (uno por evento)
28
+ #
29
+ # public final class OrderPlacedEvent extends DomainEvent {
30
+ # private final String customerId;
31
+ # private final String orderNumber;
32
+ # private final BigDecimal totalAmount;
33
+ #
34
+ # public OrderPlacedEvent(String aggregateId,
35
+ # String customerId,
36
+ # String orderNumber,
37
+ # BigDecimal totalAmount) {
38
+ # super(aggregateId);
39
+ # this.customerId = customerId;
40
+ # this.orderNumber = orderNumber;
41
+ # this.totalAmount = totalAmount;
42
+ # }
43
+ # // getters...
44
+ # }
45
+ #
46
+ # ─────────────────────────────────────────────────────────────────────────────
47
+ #
48
+ # 3. orders/domain/models/entities/Order.java (modificaciones al agregado)
49
+ #
50
+ # public class Order {
51
+ # // ... campos normales ...
52
+ #
53
+ # // ─── Domain Events ───────────────────────────────────────
54
+ # private final List<DomainEvent> _domainEvents = new ArrayList<>();
55
+ #
56
+ # protected void raise(DomainEvent event) {
57
+ # _domainEvents.add(event);
58
+ # }
59
+ #
60
+ # public List<DomainEvent> pullDomainEvents() {
61
+ # List<DomainEvent> events =
62
+ # Collections.unmodifiableList(new ArrayList<>(_domainEvents));
63
+ # _domainEvents.clear();
64
+ # return events; // retorna y limpia (operación atómica)
65
+ # }
66
+ #
67
+ # // El desarrollador llama a raise() dentro de sus métodos de negocio:
68
+ # public void place(String customerId, BigDecimal total) {
69
+ # this.status = this.status.transitionTo(OrderStatus.PLACED); // lanza InvalidStateTransitionException si no es válida
70
+ # raise(new OrderPlacedEvent(this.id, customerId, this.orderNumber, total));
71
+ # }
72
+ # }
73
+ #
74
+ # ─────────────────────────────────────────────────────────────────────────────
75
+ #
76
+ # 4. orders/infrastructure/database/repositories/OrderRepositoryImpl.java
77
+ # (publicación automática tras persistencia)
78
+ #
79
+ # @Repository
80
+ # @RequiredArgsConstructor
81
+ # public class OrderRepositoryImpl implements OrderRepository {
82
+ #
83
+ # private final OrderJpaRepository jpaRepository;
84
+ # private final OrderMapper mapper;
85
+ # private final ApplicationEventPublisher eventPublisher; // ← inyectado por Spring
86
+ #
87
+ # @Override
88
+ # public Order save(Order order) {
89
+ # OrderJpa jpa = mapper.toJpa(order);
90
+ # OrderJpa saved = jpaRepository.save(jpa); // 1. persiste en BD
91
+ # order.pullDomainEvents()
92
+ # .forEach(eventPublisher::publishEvent); // 2. publica al bus interno
93
+ # return mapper.toDomain(saved);
94
+ # }
95
+ # }
96
+ #
97
+ # ─────────────────────────────────────────────────────────────────────────────
98
+ #
99
+ # 5. orders/application/usecases/OrderDomainEventHandler.java (bridge)
100
+ #
101
+ # @Component
102
+ # public class OrderDomainEventHandler {
103
+ #
104
+ # @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
105
+ # public void onOrderPlaced(OrderPlacedEvent event) {
106
+ # // TODO: messageBroker.sendOrderPlaced(event);
107
+ # // notificationService.notify(event);
108
+ # }
109
+ #
110
+ # @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
111
+ # public void onOrderCancelled(OrderCancelledEvent event) {
112
+ # // TODO: handle OrderCancelled
113
+ # }
114
+ #
115
+ # @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
116
+ # public void onOrderShipped(OrderShippedEvent event) {
117
+ # // TODO: handle OrderShipped
118
+ # }
119
+ # }
120
+ #
121
+ # ─────────────────────────────────────────────────────────────────────────────
122
+ #
123
+ # Para conectar un broker externo después de generar:
124
+ # eva4j g kafka-event orders OrderPlaced
125
+ # (el selector muestra los eventos declarados en este domain.yaml como opciones)
126
+
127
+ aggregates:
128
+ - name: Order
129
+ entities:
130
+ - name: Order
131
+ isRoot: true
132
+ tableName: orders
133
+ audit:
134
+ enabled: true
135
+ trackUser: true
136
+ fields:
137
+ - name: id
138
+ type: String
139
+ - name: orderNumber
140
+ type: String
141
+ validations:
142
+ - type: NotBlank
143
+ - name: customerId
144
+ type: String
145
+ - name: status
146
+ type: OrderStatus
147
+ - name: totalAmount
148
+ type: BigDecimal
149
+ readOnly: true
150
+
151
+ enums:
152
+ - name: OrderStatus
153
+ values:
154
+ - DRAFT
155
+ - PLACED
156
+ - CONFIRMED
157
+ - SHIPPED
158
+ - DELIVERED
159
+ - CANCELLED
160
+ transitions:
161
+ - from: DRAFT
162
+ to: PLACED
163
+ - from: DRAFT
164
+ to: CANCELLED
165
+ - from: PLACED
166
+ to: CONFIRMED
167
+ - from: PLACED
168
+ to: CANCELLED
169
+ - from: CONFIRMED
170
+ to: SHIPPED
171
+ - from: CONFIRMED
172
+ to: CANCELLED
173
+ - from: SHIPPED
174
+ to: DELIVERED
175
+
176
+ # ─── Domain Events ────────────────────────────────────────────────────────
177
+ # Siblings de entities:, enums: y valueObjects:
178
+ # La declaración es agnóstica al broker — ningún campo de infraestructura aquí.
179
+ # Cada entrada genera: domain/models/events/{Name}Event.java
180
+
181
+ events:
182
+ - name: OrderPlaced
183
+ fields:
184
+ - name: customerId
185
+ type: String
186
+ - name: orderNumber
187
+ type: String
188
+ - name: totalAmount
189
+ type: BigDecimal
190
+
191
+ - name: OrderCancelled
192
+ fields:
193
+ - name: reason
194
+ type: String
195
+
196
+ - name: OrderShipped
197
+ fields:
198
+ - name: shippingAddress
199
+ type: String
200
+ - name: estimatedDeliveryDate
201
+ type: LocalDate
@@ -6,7 +6,7 @@ aggregates:
6
6
  # User Management Aggregate
7
7
  - name: User
8
8
  entities:
9
- - name: user
9
+ - name: User
10
10
  isRoot: true
11
11
  tableName: users
12
12
  audit:
@@ -40,6 +40,7 @@ aggregates:
40
40
  - name: loginCount
41
41
  type: Integer
42
42
  readOnly: true
43
+ defaultValue: 0 # Initialized to 0 on creation
43
44
 
44
45
  # Readonly field - NOT in constructor or CreateDto
45
46
  # Use case: Calculated timestamp
@@ -57,7 +58,7 @@ aggregates:
57
58
  # Order Management Aggregate
58
59
  - name: Order
59
60
  entities:
60
- - name: order
61
+ - name: Order
61
62
  isRoot: true
62
63
  tableName: orders
63
64
  audit:
@@ -80,11 +81,13 @@ aggregates:
80
81
  - name: totalAmount
81
82
  type: BigDecimal
82
83
  readOnly: true
84
+ defaultValue: "0.00" # Starts at zero before items are added
83
85
 
84
86
  # Readonly field - count of items
85
87
  - name: itemCount
86
88
  type: Integer
87
89
  readOnly: true
90
+ defaultValue: 0 # Starts at zero
88
91
 
89
92
  # Hidden field - internal processing flag
90
93
  - name: processingToken
@@ -97,7 +100,7 @@ aggregates:
97
100
  mappedBy: order
98
101
  cascade: [PERSIST, MERGE, REMOVE]
99
102
 
100
- - name: orderItem
103
+ - name: OrderItem
101
104
  tableName: order_items
102
105
  fields:
103
106
  - name: id
@@ -116,6 +119,7 @@ aggregates:
116
119
  - name: subtotal
117
120
  type: BigDecimal
118
121
  readOnly: true
122
+ defaultValue: "0.00" # Calculated on creation
119
123
 
120
124
  relationships:
121
125
  - type: ManyToOne
@@ -125,7 +129,7 @@ aggregates:
125
129
  # Product Catalog Aggregate
126
130
  - name: Product
127
131
  entities:
128
- - name: product
132
+ - name: Product
129
133
  isRoot: true
130
134
  tableName: products
131
135
  audit:
@@ -147,6 +151,7 @@ aggregates:
147
151
  - name: stockLevel
148
152
  type: Integer
149
153
  readOnly: true
154
+ defaultValue: 0 # New products start with zero stock
150
155
 
151
156
  # Readonly field - last updated stock timestamp
152
157
  - name: stockUpdatedAt
@@ -166,7 +171,7 @@ aggregates:
166
171
  # Session Management Aggregate
167
172
  - name: Session
168
173
  entities:
169
- - name: session
174
+ - name: Session
170
175
  isRoot: true
171
176
  tableName: sessions
172
177
  fields:
@@ -191,6 +196,7 @@ aggregates:
191
196
  type: Boolean
192
197
  hidden: true
193
198
  readOnly: true
199
+ defaultValue: false # Sessions start as not revoked
194
200
 
195
201
  - name: createdAt
196
202
  type: LocalDateTime
@@ -6,7 +6,7 @@ aggregates:
6
6
  # AGREGADO 1: Customer
7
7
  - name: Customer
8
8
  entities:
9
- - name: customer
9
+ - name: Customer
10
10
  isRoot: true
11
11
  tableName: customers
12
12
  auditable: true
@@ -30,7 +30,7 @@ aggregates:
30
30
  cascade: [PERSIST, MERGE, REMOVE]
31
31
  fetch: LAZY
32
32
 
33
- - name: address
33
+ - name: Address
34
34
  tableName: customer_addresses
35
35
  fields:
36
36
  - name: id
@@ -77,7 +77,7 @@ aggregates:
77
77
  # AGREGADO 2: Supplier
78
78
  - name: Supplier
79
79
  entities:
80
- - name: supplier
80
+ - name: Supplier
81
81
  isRoot: true
82
82
  tableName: suppliers
83
83
  auditable: true
@@ -97,7 +97,7 @@ aggregates:
97
97
  - name: rating
98
98
  type: Integer
99
99
 
100
- - name: contract
100
+ - name: Contract
101
101
  tableName: contracts
102
102
  auditable: true
103
103
  fields:
@@ -133,7 +133,7 @@ aggregates:
133
133
  # AGREGADO 3: Inventory
134
134
  - name: Inventory
135
135
  entities:
136
- - name: inventory
136
+ - name: Inventory
137
137
  isRoot: true
138
138
  tableName: inventory
139
139
  auditable: true
@@ -142,8 +142,14 @@ aggregates:
142
142
  type: String
143
143
  - name: productId
144
144
  type: String
145
+ reference:
146
+ aggregate: Product
147
+ module: catalog
145
148
  - name: warehouseId
146
149
  type: String
150
+ reference:
151
+ aggregate: Warehouse
152
+ module: warehouses
147
153
  - name: quantity
148
154
  type: Integer
149
155
  - name: reservedQuantity
@@ -163,7 +169,7 @@ aggregates:
163
169
  cascade: [PERSIST, MERGE]
164
170
  fetch: LAZY
165
171
 
166
- - name: stockMovement
172
+ - name: StockMovement
167
173
  tableName: stock_movements
168
174
  auditable: true
169
175
  fields:
@@ -5,7 +5,7 @@
5
5
  aggregates:
6
6
  - name: Order
7
7
  entities:
8
- - name: order
8
+ - name: Order
9
9
  isRoot: true
10
10
  tableName: orders
11
11
  audit:
@@ -29,7 +29,7 @@ aggregates:
29
29
  relationships:
30
30
  - type: OneToOne
31
31
  target: UserProfile
32
- mappedBy: User
32
+ mappedBy: user
33
33
  cascade: [PERSIST, MERGE, REMOVE]
34
34
  fetch: LAZY
35
35
 
@@ -11,7 +11,7 @@
11
11
  aggregates:
12
12
  - name: Project
13
13
  entities:
14
- - name: project
14
+ - name: Project
15
15
  isRoot: true
16
16
  tableName: projects
17
17
  audit:
@@ -11,7 +11,7 @@
11
11
  aggregates:
12
12
  - name: Order
13
13
  entities:
14
- - name: order
14
+ - name: Order
15
15
  isRoot: true
16
16
  tableName: orders
17
17
  audit:
@@ -6,7 +6,7 @@
6
6
  aggregates:
7
7
  - name: Product
8
8
  entities:
9
- - name: product
9
+ - name: Product
10
10
  isRoot: true
11
11
  tableName: products
12
12
  auditable: true
@@ -5,7 +5,7 @@
5
5
  aggregates:
6
6
  - name: Document
7
7
  entities:
8
- - name: document
8
+ - name: Document
9
9
  isRoot: true
10
10
  tableName: documents
11
11
  auditable: true
@@ -48,7 +48,7 @@ aggregates:
48
48
  cascade: [PERSIST, MERGE, REMOVE]
49
49
  fetch: LAZY
50
50
 
51
- - name: revision
51
+ - name: Revision
52
52
  tableName: document_revisions
53
53
  auditable: true
54
54
  fields:
@@ -66,7 +66,7 @@ aggregates:
66
66
  type: String
67
67
 
68
68
 
69
- - name: attachment
69
+ - name: Attachment
70
70
  tableName: document_attachments
71
71
  auditable: true
72
72
  fields:
@@ -1,7 +1,7 @@
1
1
  aggregates:
2
2
  - name: Order
3
3
  entities:
4
- - name: order
4
+ - name: Order
5
5
  isRoot: true
6
6
  tableName: orders
7
7
  audit:
@@ -5,7 +5,7 @@
5
5
  aggregates:
6
6
  - name: User
7
7
  entities:
8
- - name: user
8
+ - name: User
9
9
  isRoot: true
10
10
  tableName: users
11
11
  auditable: true
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "eva4j",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "A powerful Node.js CLI for generating Spring Boot projects with modular architecture that enables efficient monolith-first development with seamless transition to microservices",
5
5
  "main": "bin/eva4j.js",
6
6
  "bin": {
7
- "eva4j": "./bin/eva4j.js"
7
+ "eva": "./bin/eva4j.js"
8
8
  },
9
9
  "scripts": {
10
10
  "start": "node bin/eva4j.js",
@@ -7,6 +7,7 @@ const ConfigManager = require('../utils/config-manager');
7
7
  const { isEva4jProject } = require('../utils/validator');
8
8
  const { toPackagePath } = require('../utils/naming');
9
9
  const { renderAndWrite, renderTemplate } = require('../utils/template-engine');
10
+ const defaults = require('../../config/defaults.json');
10
11
 
11
12
  async function addKafkaClientCommand() {
12
13
  const projectDir = process.cwd();
@@ -53,7 +54,8 @@ async function addKafkaClientCommand() {
53
54
  packagePath,
54
55
  projectName,
55
56
  groupId,
56
- artifactId
57
+ artifactId,
58
+ kafkaConfluentVersion: defaults.kafkaConfluentVersion
57
59
  };
58
60
 
59
61
  // 1. Add dependencies to build.gradle