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
@@ -1,498 +1,219 @@
1
- # Command `generate kafka-event` (alias: `g kafka-event`)
2
-
3
- ## 📋 Description
4
-
5
- Generates infrastructure for publishing domain events to Apache Kafka topics, enabling asynchronous event-driven communication between modules or microservices.
6
-
7
- ## 🎯 Purpose
8
-
9
- Implement event-driven architecture by publishing domain events to Kafka, allowing other services to react to business events without direct coupling.
10
-
11
- ## 📝 Syntax
12
-
13
- ```bash
14
- eva4j generate kafka-event <EventName>
15
- eva4j g kafka-event <EventName> # Short alias
16
- ```
17
-
18
- ### Parameters
19
-
20
- | Parameter | Required | Description |
21
- |-----------|----------|-------------|
22
- | `EventName` | Yes | Name of the event (PascalCase, e.g., OrderCreated, PaymentProcessed) |
23
-
24
- ## 💡 Examples
25
-
26
- ### Example 1: Order Created Event
27
-
28
- ```bash
29
- eva4j g kafka-event OrderCreated
30
- ```
31
-
32
- ### Example 2: Payment Processed Event
33
-
34
- ```bash
35
- eva4j g kafka-event PaymentProcessed
36
- ```
37
-
38
- ### Example 3: User Registered Event
39
-
40
- ```bash
41
- eva4j g kafka-event UserRegistered
42
- ```
43
-
44
- ## 📦 Generated Code Structure
45
-
46
- ```
47
- <module>/
48
- ├── domain/
49
- │ └── events/
50
- │ └── OrderCreatedEvent.java # Domain event
51
-
52
- └── infrastructure/
53
- └── messaging/
54
- ├── events/
55
- │ └── OrderCreatedKafkaEvent.java # Kafka DTO
56
- ├── publishers/
57
- │ └── OrderEventPublisher.java # Event publisher
58
- └── config/
59
- └── KafkaProducerConfig.java # Kafka configuration
60
- ```
61
-
62
- ## 📄 Generated Files
63
-
64
- ### 1. Domain Event
65
-
66
- **OrderCreatedEvent.java:**
67
- ```java
68
- package com.example.project.order.domain.events;
69
-
70
- import lombok.AllArgsConstructor;
71
- import lombok.Data;
72
- import lombok.NoArgsConstructor;
73
- import java.time.LocalDateTime;
74
-
75
- /**
76
- * Domain event: OrderCreated
77
- */
78
- @Data
79
- @NoArgsConstructor
80
- @AllArgsConstructor
81
- public class OrderCreatedEvent {
82
-
83
- private String eventId;
84
- private LocalDateTime occurredOn;
85
-
86
- // Add your event data here
87
- private Long orderId;
88
- private Long customerId;
89
- private String orderNumber;
90
-
91
- public OrderCreatedEvent(Long orderId, Long customerId, String orderNumber) {
92
- this.eventId = java.util.UUID.randomUUID().toString();
93
- this.occurredOn = LocalDateTime.now();
94
- this.orderId = orderId;
95
- this.customerId = customerId;
96
- this.orderNumber = orderNumber;
97
- }
98
- }
99
- ```
100
-
101
- ### 2. Kafka Event DTO
102
-
103
- **OrderCreatedKafkaEvent.java:**
104
- ```java
105
- package com.example.project.order.infrastructure.messaging.events;
106
-
107
- import lombok.AllArgsConstructor;
108
- import lombok.Data;
109
- import lombok.NoArgsConstructor;
110
-
111
- /**
112
- * Kafka DTO for OrderCreated event
113
- */
114
- @Data
115
- @NoArgsConstructor
116
- @AllArgsConstructor
117
- public class OrderCreatedKafkaEvent {
118
-
119
- private String eventId;
120
- private String occurredOn;
121
- private Long orderId;
122
- private Long customerId;
123
- private String orderNumber;
124
-
125
- // Add metadata
126
- private String eventType = "OrderCreated";
127
- private String version = "1.0";
128
- }
129
- ```
130
-
131
- ### 3. Event Publisher
132
-
133
- **OrderEventPublisher.java:**
134
- ```java
135
- package com.example.project.order.infrastructure.messaging.publishers;
136
-
137
- import com.example.project.order.domain.events.OrderCreatedEvent;
138
- import com.example.project.order.infrastructure.messaging.events.OrderCreatedKafkaEvent;
139
- import lombok.RequiredArgsConstructor;
140
- import lombok.extern.slf4j.Slf4j;
141
- import org.springframework.kafka.core.KafkaTemplate;
142
- import org.springframework.kafka.support.SendResult;
143
- import org.springframework.stereotype.Component;
144
-
145
- import java.util.concurrent.CompletableFuture;
146
-
147
- /**
148
- * Publisher for Order events
149
- */
150
- @Slf4j
151
- @Component
152
- @RequiredArgsConstructor
153
- public class OrderEventPublisher {
154
-
155
- private final KafkaTemplate<String, OrderCreatedKafkaEvent> kafkaTemplate;
156
-
157
- private static final String ORDER_CREATED_TOPIC = "order.created";
158
-
159
- public void publishOrderCreated(OrderCreatedEvent event) {
160
- OrderCreatedKafkaEvent kafkaEvent = mapToKafkaEvent(event);
161
-
162
- CompletableFuture<SendResult<String, OrderCreatedKafkaEvent>> future =
163
- kafkaTemplate.send(ORDER_CREATED_TOPIC, event.getOrderId().toString(), kafkaEvent);
164
-
165
- future.whenComplete((result, ex) -> {
166
- if (ex == null) {
167
- log.info("OrderCreated event published successfully: orderId={}, offset={}",
168
- event.getOrderId(), result.getRecordMetadata().offset());
169
- } else {
170
- log.error("Failed to publish OrderCreated event: orderId={}",
171
- event.getOrderId(), ex);
172
- }
173
- });
174
- }
175
-
176
- private OrderCreatedKafkaEvent mapToKafkaEvent(OrderCreatedEvent event) {
177
- return new OrderCreatedKafkaEvent(
178
- event.getEventId(),
179
- event.getOccurredOn().toString(),
180
- event.getOrderId(),
181
- event.getCustomerId(),
182
- event.getOrderNumber()
183
- );
184
- }
185
- }
186
- ```
187
-
188
- ### 4. Kafka Configuration
189
-
190
- **KafkaProducerConfig.java:**
191
- ```java
192
- package com.example.project.order.infrastructure.messaging.config;
193
-
194
- import org.apache.kafka.clients.producer.ProducerConfig;
195
- import org.apache.kafka.common.serialization.StringSerializer;
196
- import org.springframework.beans.factory.annotation.Value;
197
- import org.springframework.context.annotation.Bean;
198
- import org.springframework.context.annotation.Configuration;
199
- import org.springframework.kafka.core.DefaultKafkaProducerFactory;
200
- import org.springframework.kafka.core.KafkaTemplate;
201
- import org.springframework.kafka.core.ProducerFactory;
202
- import org.springframework.kafka.support.serializer.JsonSerializer;
203
-
204
- import java.util.HashMap;
205
- import java.util.Map;
206
-
207
- /**
208
- * Kafka producer configuration
209
- */
210
- @Configuration
211
- public class KafkaProducerConfig {
212
-
213
- @Value("${spring.kafka.bootstrap-servers}")
214
- private String bootstrapServers;
215
-
216
- @Bean
217
- public ProducerFactory<String, Object> producerFactory() {
218
- Map<String, Object> config = new HashMap<>();
219
- config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
220
- config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
221
- config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
222
- config.put(ProducerConfig.ACKS_CONFIG, "all");
223
- config.put(ProducerConfig.RETRIES_CONFIG, 3);
224
- config.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
225
- return new DefaultKafkaProducerFactory<>(config);
226
- }
227
-
228
- @Bean
229
- public KafkaTemplate<String, Object> kafkaTemplate() {
230
- return new KafkaTemplate<>(producerFactory());
231
- }
232
- }
233
- ```
234
-
235
- ## ✨ Features
236
-
237
- ### Event Publishing
238
- - ✅ **Asynchronous** - Non-blocking event publishing
239
- - ✅ **Idempotent** - Prevents duplicate events
240
- - ✅ **Reliable** - Acknowledgment and retries
241
- - ✅ **Partitioned** - Uses orderId as partition key
242
- - ✅ **Logged** - Success/failure logging
243
-
244
- ### Event Structure
245
- - ✅ **Event ID** - Unique identifier for deduplication
246
- - ✅ **Timestamp** - When event occurred
247
- - ✅ **Type** - Event type identifier
248
- - ✅ **Version** - Schema versioning
249
- - ✅ **Payload** - Business data
250
-
251
- ## 🔧 Configuration
252
-
253
- ### Application Properties
254
-
255
- ```yaml
256
- # application.yaml
257
-
258
- spring:
259
- kafka:
260
- bootstrap-servers: localhost:9092
261
- producer:
262
- key-serializer: org.apache.kafka.common.serialization.StringSerializer
263
- value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
264
- acks: all
265
- retries: 3
266
- properties:
267
- enable.idempotence: true
268
- max.in.flight.requests.per.connection: 5
269
-
270
- # Topic configuration
271
- kafka:
272
- topics:
273
- order-created: order.created
274
- payment-processed: payment.processed
275
- ```
276
-
277
- ## 🎯 Usage Examples
278
-
279
- ### 1. Publish from Command Handler
280
-
281
- ```java
282
- @Service
283
- @RequiredArgsConstructor
284
- public class CreateOrderCommandHandler {
285
-
286
- private final OrderRepository repository;
287
- private final OrderEventPublisher eventPublisher;
288
-
289
- @Transactional
290
- public OrderResponseDto handle(CreateOrderCommand command) {
291
- // Create order
292
- Order order = Order.create(command.getCustomerId(), command.getItems());
293
- Order savedOrder = repository.save(order);
294
-
295
- // Publish event
296
- OrderCreatedEvent event = new OrderCreatedEvent(
297
- savedOrder.getId(),
298
- savedOrder.getCustomerId(),
299
- savedOrder.getOrderNumber()
300
- );
301
- eventPublisher.publishOrderCreated(event);
302
-
303
- return OrderMapper.toDto(savedOrder);
304
- }
305
- }
306
- ```
307
-
308
- ### 2. Publish from Domain Entity
309
-
310
- ```java
311
- @Entity
312
- public class Order {
313
-
314
- @Transient
315
- private final List<DomainEvent> domainEvents = new ArrayList<>();
316
-
317
- public static Order create(Long customerId, List<OrderItem> items) {
318
- Order order = new Order();
319
- order.setCustomerId(customerId);
320
- order.setItems(items);
321
-
322
- // Register domain event
323
- order.registerEvent(new OrderCreatedEvent(
324
- order.getId(),
325
- order.getCustomerId(),
326
- order.getOrderNumber()
327
- ));
328
-
329
- return order;
330
- }
331
-
332
- private void registerEvent(DomainEvent event) {
333
- domainEvents.add(event);
334
- }
335
-
336
- public List<DomainEvent> getDomainEvents() {
337
- return Collections.unmodifiableList(domainEvents);
338
- }
339
- }
340
-
341
- // In handler
342
- @Transactional
343
- public void handle(CreateOrderCommand command) {
344
- Order order = Order.create(command.getCustomerId(), command.getItems());
345
- Order savedOrder = repository.save(order);
346
-
347
- // Publish collected events
348
- savedOrder.getDomainEvents().forEach(event -> {
349
- if (event instanceof OrderCreatedEvent) {
350
- eventPublisher.publishOrderCreated((OrderCreatedEvent) event);
351
- }
352
- });
353
- }
354
- ```
355
-
356
- ### 3. Publish Multiple Events
357
-
358
- ```java
359
- @Service
360
- @RequiredArgsConstructor
361
- public class OrderEventPublisher {
362
-
363
- private final KafkaTemplate<String, Object> kafkaTemplate;
364
-
365
- public void publishOrderCreated(OrderCreatedEvent event) {
366
- kafkaTemplate.send("order.created", event.getOrderId().toString(),
367
- mapToKafkaEvent(event));
368
- }
369
-
370
- public void publishOrderCancelled(OrderCancelledEvent event) {
371
- kafkaTemplate.send("order.cancelled", event.getOrderId().toString(),
372
- mapToKafkaEvent(event));
373
- }
374
-
375
- public void publishOrderShipped(OrderShippedEvent event) {
376
- kafkaTemplate.send("order.shipped", event.getOrderId().toString(),
377
- mapToKafkaEvent(event));
378
- }
379
- }
380
- ```
381
-
382
- ## 🔄 Event-Driven Patterns
383
-
384
- ### 1. Event Notification
385
-
386
- ```java
387
- // Order service publishes
388
- eventPublisher.publishOrderCreated(event);
389
-
390
- // Inventory service listens and reacts
391
- @KafkaListener(topics = "order.created")
392
- public void handleOrderCreated(OrderCreatedKafkaEvent event) {
393
- inventoryService.reserveStock(event.getOrderId());
394
- }
395
- ```
396
-
397
- ### 2. Event-Carried State Transfer
398
-
399
- ```java
400
- // Include all necessary data in event
401
- public class OrderCreatedEvent {
402
- private Long orderId;
403
- private Long customerId;
404
- private List<OrderItem> items; // Full state
405
- private BigDecimal totalAmount;
406
- private String shippingAddress;
407
- }
408
- ```
409
-
410
- ### 3. Event Sourcing (Advanced)
411
-
412
- ```java
413
- // Store events as source of truth
414
- public class OrderEventStore {
415
- public void save(OrderCreatedEvent event) {
416
- // Persist event to event store
417
- }
418
-
419
- public Order rebuild(Long orderId) {
420
- // Replay events to rebuild state
421
- List<DomainEvent> events = loadEvents(orderId);
422
- return Order.fromEvents(events);
423
- }
424
- }
425
- ```
426
-
427
- ## 🚀 Next Steps
428
-
429
- After generating Kafka event infrastructure:
430
-
431
- 1. **Define event payload:**
432
- ```java
433
- public class OrderCreatedEvent {
434
- private Long orderId;
435
- private Long customerId;
436
- private BigDecimal totalAmount;
437
- private List<OrderItemDto> items;
438
- }
439
- ```
440
-
441
- 2. **Publish from business logic:**
442
- ```java
443
- eventPublisher.publishOrderCreated(event);
444
- ```
445
-
446
- 3. **Create listeners in other modules:**
447
- ```bash
448
- eva4j g kafka-listener OrderCreated
449
- ```
450
-
451
- 4. **Monitor events:**
452
- - Use Kafka UI (http://localhost:8080 if using docker-compose)
453
- - Check logs for publish confirmations
454
-
455
- ## ⚠️ Prerequisites
456
-
457
- - Be in a project created with `eva4j create`
458
- - Module must exist
459
- - Kafka must be running (use `docker-compose up -d`)
460
- - Spring Kafka dependency (automatically added)
461
-
462
- ## 🔍 Validations
463
-
464
- The command validates:
465
- - ✅ Valid eva4j project
466
- - ✅ Event name is in PascalCase
467
- - ✅ Module exists
468
- - ✅ Kafka is configured
469
-
470
- ## 📚 See Also
471
-
472
- - [generate-kafka-listener](./GENERATE_KAFKA_LISTENER.md) - Consume Kafka events
473
- - [add-kafka-client](./ADD_KAFKA_CLIENT.md) - Add Kafka to module
474
- - [generate-entities](./GENERATE_ENTITIES.md) - Generate domain models
475
-
476
- ## 🐛 Troubleshooting
477
-
478
- **Error: "Kafka connection refused"**
479
- - Solution: Ensure Kafka is running: `docker-compose up -d kafka`
480
-
481
- **Events not appearing in topic**
482
- - Solution: Check Kafka logs and verify topic exists
483
- ```bash
484
- docker exec -it kafka kafka-topics --list --bootstrap-server localhost:9092
485
- ```
486
-
487
- **Serialization errors**
488
- - Solution: Ensure event classes are serializable and have no-arg constructor
489
-
490
- **Lost events**
491
- - Solution: Configure appropriate `acks` and `retries`:
492
- ```yaml
493
- spring:
494
- kafka:
495
- producer:
496
- acks: all
497
- retries: 3
498
- ```
1
+ # Command `generate kafka-event` (alias: `g kafka-event`)
2
+
3
+ ## Description
4
+
5
+ Generates the infrastructure for publishing a domain event to an Apache Kafka topic, enabling asynchronous event-driven communication between modules or microservices.
6
+
7
+ ## Purpose
8
+
9
+ Implement event publishing without coupling the sender to the consumer. The generated `MessageBroker` port keeps the domain free of Kafka dependencies, while the adapter handles the actual publishing using `EventEnvelope` for correlation tracking.
10
+
11
+ ## Syntax
12
+
13
+ ```bash
14
+ eva generate kafka-event <module> [event-name]
15
+ eva g kafka-event <module> [event-name] # Short alias
16
+ ```
17
+
18
+ ### Parameters
19
+
20
+ | Parameter | Required | Description |
21
+ |-----------|----------|-------------|
22
+ | `module` | Yes | Module that owns and publishes the event (e.g., `user`, `order`) |
23
+ | `event-name` | No | Event name in kebab-case — prompted if omitted; `Event` suffix is added automatically |
24
+
25
+ > **Interactive prompts:**
26
+ > 1. **Event name** — if not provided (e.g., `user-created`, `order-placed`)
27
+ > 2. **Topic name** — Kafka topic identifier (e.g., `USER_CREATED`)
28
+ > 3. **Number of partitions** — default: 3
29
+ > 4. **Number of replicas** — default: 1
30
+
31
+ **Note:** Running the command multiple times on the same module safely **appends** a new method to the existing `MessageBroker.java` without overwriting it.
32
+
33
+ ## Prerequisites
34
+
35
+ Kafka client must be installed in the project:
36
+
37
+ ```bash
38
+ eva add kafka-client
39
+ ```
40
+
41
+ ## Examples
42
+
43
+ ### Example 1: User created event
44
+
45
+ ```bash
46
+ eva g kafka-event user user-created
47
+ # Event class: UserCreatedEvent
48
+ # Topic: USER_CREATED (entered at prompt)
49
+ ```
50
+
51
+ Generates:
52
+ - `user/application/events/UserCreatedEvent.java`
53
+ - `user/application/ports/MessageBroker.java` (created or appended)
54
+ - `user/infrastructure/adapters/kafkaMessageBroker/UserKafkaMessageBroker.java`
55
+ - Adds topic config to `parameters/*/kafka.yaml`
56
+
57
+ ### Example 2: Order placed event
58
+
59
+ ```bash
60
+ eva g kafka-event order order-placed
61
+ ```
62
+
63
+ ### Example 3: Multiple events in the same module
64
+
65
+ ```bash
66
+ eva g kafka-event user user-created # Creates MessageBroker with publishUserCreatedEvent
67
+ eva g kafka-event user user-updated # Appends publishUserUpdatedEvent to MessageBroker
68
+ eva g kafka-event user user-deleted # Appends publishUserDeletedEvent to MessageBroker
69
+ ```
70
+
71
+ Each run adds a new method without overwriting existing ones.
72
+
73
+ ## Generated Code Structure
74
+
75
+ ```
76
+ <module>/
77
+ ├── application/
78
+ │ ├── events/
79
+ │ │ └── UserCreatedEvent.java # Java record (event data)
80
+ │ └── ports/
81
+ │ └── MessageBroker.java # Port interface (append-safe)
82
+
83
+ └── infrastructure/
84
+ └── adapters/
85
+ └── kafkaMessageBroker/
86
+ └── UserKafkaMessageBroker.java # Kafka adapter
87
+ ```
88
+
89
+ ## Generated Files
90
+
91
+ ### 1. Event (Application Layer)
92
+
93
+ **UserCreatedEvent.java** (`application/events/`):
94
+ ```java
95
+ package com.example.project.user.application.events;
96
+
97
+ public record UserCreatedEvent(
98
+ // TODO: Add your event fields here
99
+ // Example:
100
+ // Long id,
101
+ // String name,
102
+ // LocalDateTime createdAt
103
+ ) {
104
+ }
105
+ ```
106
+
107
+ > Events are pure Java records — immutable, no Lombok, no framework annotations.
108
+
109
+ ### 2. MessageBroker Port (Application Layer)
110
+
111
+ **MessageBroker.java** (`application/ports/`):
112
+ ```java
113
+ package com.example.project.user.application.ports;
114
+
115
+ import com.example.project.user.application.events.UserCreatedEvent;
116
+
117
+ public interface MessageBroker {
118
+
119
+ void publishUserCreatedEvent(UserCreatedEvent event);
120
+ }
121
+ ```
122
+
123
+ > If `MessageBroker.java` already exists (from a previous run), the new `publishXxx` method is **appended** to the interface rather than overwriting it.
124
+
125
+ ### 3. Kafka Adapter (Infrastructure)
126
+
127
+ **UserKafkaMessageBroker.java** (`infrastructure/adapters/kafkaMessageBroker/`):
128
+ ```java
129
+ package com.example.project.user.infrastructure.adapters.kafkaMessageBroker;
130
+
131
+ import com.example.project.user.application.events.UserCreatedEvent;
132
+ import com.example.project.user.application.ports.MessageBroker;
133
+ import com.example.project.shared.infrastructure.eventEnvelope.EventEnvelope;
134
+ import org.springframework.beans.factory.annotation.Value;
135
+ import org.springframework.kafka.core.KafkaTemplate;
136
+ import org.springframework.stereotype.Component;
137
+ import org.slf4j.MDC;
138
+
139
+ @Component("userKafkaMessageBroker")
140
+ public class UserKafkaMessageBroker implements MessageBroker {
141
+
142
+ @Value("${topics.user-created}")
143
+ private String usercreatedTopic;
144
+
145
+ private final KafkaTemplate<String, Object> kafkaTemplate;
146
+
147
+ public UserKafkaMessageBroker(KafkaTemplate<String, Object> kafkaTemplate) {
148
+ this.kafkaTemplate = kafkaTemplate;
149
+ }
150
+
151
+ @Override
152
+ public void publishUserCreatedEvent(UserCreatedEvent event) {
153
+ EventEnvelope<UserCreatedEvent> envelope = EventEnvelope.of(
154
+ usercreatedTopic,
155
+ event,
156
+ MDC.get("correlationId")
157
+ );
158
+ kafkaTemplate.send(usercreatedTopic, envelope);
159
+ }
160
+ }
161
+ ```
162
+
163
+ > `EventEnvelope.of(topic, payload, correlationId)` wraps the event with routing metadata and the current MDC correlation ID for distributed tracing.
164
+
165
+ ## Configuration Added
166
+
167
+ ```yaml
168
+ # parameters/local/kafka.yaml
169
+ spring.kafka:
170
+ topics:
171
+ user-created: USER_CREATED
172
+ ```
173
+
174
+ The same entry is added to every environment (`local`, `develop`, `test`, `production`).
175
+
176
+ ## Usage in a Command Handler
177
+
178
+ Inject `MessageBroker` (the port, not the Kafka class) and call the publish method:
179
+
180
+ ```java
181
+ @ApplicationComponent
182
+ public class CreateUserCommandHandler {
183
+
184
+ private final UserRepository userRepository;
185
+ private final MessageBroker messageBroker;
186
+
187
+ public CreateUserCommandHandler(UserRepository userRepository, MessageBroker messageBroker) {
188
+ this.userRepository = userRepository;
189
+ this.messageBroker = messageBroker;
190
+ }
191
+
192
+ public void handle(CreateUserCommand command) {
193
+ User user = new User(command.name(), command.email());
194
+ userRepository.save(user);
195
+
196
+ messageBroker.publishUserCreatedEvent(
197
+ new UserCreatedEvent(/* fill fields */)
198
+ );
199
+ }
200
+ }
201
+ ```
202
+
203
+ ## Bean Naming
204
+
205
+ The adapter is registered with an explicit bean name to avoid conflicts when multiple modules have a `MessageBroker`:
206
+
207
+ | Module | Bean name |
208
+ |--------|-----------|
209
+ | `user` | `userKafkaMessageBroker` |
210
+ | `order` | `orderKafkaMessageBroker` |
211
+ | `product` | `productKafkaMessageBroker` |
212
+
213
+ Spring resolves the correct adapter for each module via constructor injection by type + qualifier if needed.
214
+
215
+ ## See Also
216
+
217
+ - [generate-kafka-listener](./GENERATE_KAFKA_LISTENER.md) — Create Kafka event consumer
218
+ - [add-kafka-client](./ADD_MODULE.md) Install Kafka dependencies
219
+ - [generate-usecase](./GENERATE_USECASE.md) — Create use case handlers