eva4j 1.0.17 → 1.0.18
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.
- package/AGENTS.md +2 -0
- package/DOMAIN_YAML_GUIDE.md +3 -1
- package/QUICK_REFERENCE.md +8 -4
- package/bin/eva4j.js +70 -2
- package/config/defaults.json +1 -0
- package/docs/CAMUNDA_DMN_GUIDE.md +1380 -0
- package/docs/KAFKA_PRODUCTION_CONFIG.md +441 -0
- package/docs/RABBITMQ_PRODUCTION_CONFIG.md +227 -0
- package/docs/commands/ADD_RABBITMQ_CLIENT.md +192 -0
- package/docs/commands/EVALUATE_SYSTEM.md +272 -8
- package/docs/commands/GENERATE_RABBITMQ_EVENT.md +341 -0
- package/docs/commands/GENERATE_RABBITMQ_LISTENER.md +595 -0
- package/docs/commands/GENERATE_TEMPORAL_FLOW.md +52 -12
- package/docs/commands/INDEX.md +27 -3
- package/docs/prototype/TEMPORAL_COMMUNICATION_PATTERNS.md +731 -0
- package/docs/prototype/TEMPORAL_DESIGN_METHODOLOGY.md +740 -0
- package/docs/prototype/system/RISKS.md +277 -0
- package/docs/prototype/system/customers.yaml +133 -0
- package/docs/prototype/system/inventory.yaml +109 -0
- package/docs/prototype/system/notifications.yaml +131 -0
- package/docs/prototype/system/orders.yaml +241 -0
- package/docs/prototype/system/payments.yaml +256 -0
- package/docs/prototype/system/products.yaml +168 -0
- package/docs/prototype/system/system.yaml +269 -0
- package/examples/domain-endpoints-multi-aggregate.yaml +140 -0
- package/examples/domain-read-models.yaml +2 -2
- package/examples/system/customer.yaml +89 -0
- package/examples/system/orders.yaml +119 -0
- package/examples/system/product.yaml +27 -0
- package/examples/system/system.yaml +80 -0
- package/package.json +1 -1
- package/src/agents/design-gap-analyst-temporal.agent.md +452 -0
- package/src/agents/design-gap-analyst.agent.md +383 -0
- package/src/agents/design-reviewer-temporal.agent.md +412 -0
- package/src/agents/design-reviewer.agent.md +31 -5
- package/src/agents/implement-use-cases.prompt.md +179 -0
- package/src/agents/ux-gap-analyst.agent.md +412 -0
- package/src/commands/add-rabbitmq-client.js +261 -0
- package/src/commands/add-temporal-client.js +22 -2
- package/src/commands/build.js +267 -11
- package/src/commands/evaluate-system.js +700 -13
- package/src/commands/generate-entities.js +308 -16
- package/src/commands/generate-rabbitmq-event.js +665 -0
- package/src/commands/generate-rabbitmq-listener.js +205 -0
- package/src/commands/generate-temporal-activity.js +968 -34
- package/src/commands/generate-temporal-flow.js +95 -38
- package/src/commands/generate-temporal-system.js +708 -0
- package/src/skills/build-system-yaml/SKILL.md +222 -2
- package/src/skills/build-system-yaml/references/domain-yaml-spec.md +50 -4
- package/src/skills/build-system-yaml/references/module-spec.md +57 -8
- package/src/skills/build-temporal-system/SKILL.md +752 -0
- package/src/skills/build-temporal-system/references/temporal-communication-patterns.md +167 -0
- package/src/skills/build-temporal-system/references/temporal-domain-yaml-spec.md +449 -0
- package/src/skills/build-temporal-system/references/temporal-module-spec.md +353 -0
- package/src/skills/build-temporal-system/references/temporal-system-yaml-spec.md +326 -0
- package/src/skills/implement-use-case/SKILL.md +350 -0
- package/src/skills/implement-use-case/references/use-case-patterns.md +980 -0
- package/src/skills/requirements-elicitation/SKILL.md +228 -0
- package/src/skills/requirements-elicitation/references/interview-framework.md +260 -0
- package/src/skills/requirements-elicitation/references/output-templates.md +368 -0
- package/src/utils/bounded-context-diagram.js +844 -0
- package/src/utils/domain-validator.js +266 -4
- package/src/utils/naming.js +10 -0
- package/src/utils/system-validator.js +169 -11
- package/src/utils/system-yaml-parser.js +318 -0
- package/src/utils/temporal-validator.js +497 -0
- package/src/utils/yaml-to-entity.js +10 -7
- package/templates/aggregate/DomainEventHandler.java.ejs +116 -22
- package/templates/aggregate/JpaAggregateRoot.java.ejs +2 -2
- package/templates/aggregate/JpaEntity.java.ejs +2 -2
- package/templates/base/docker/rabbitmq-services.yaml.ejs +12 -0
- package/templates/base/resources/parameters/develop/kafka.yaml.ejs +5 -0
- package/templates/base/resources/parameters/develop/rabbitmq.yaml.ejs +15 -0
- package/templates/base/resources/parameters/develop/temporal.yaml.ejs +0 -3
- package/templates/base/resources/parameters/local/kafka.yaml.ejs +5 -0
- package/templates/base/resources/parameters/local/rabbitmq.yaml.ejs +15 -0
- package/templates/base/resources/parameters/local/temporal.yaml.ejs +0 -3
- package/templates/base/resources/parameters/production/kafka.yaml.ejs +39 -8
- package/templates/base/resources/parameters/production/rabbitmq.yaml.ejs +32 -0
- package/templates/base/resources/parameters/production/temporal.yaml.ejs +0 -3
- package/templates/base/resources/parameters/test/kafka.yaml.ejs +12 -6
- package/templates/base/resources/parameters/test/rabbitmq.yaml.ejs +15 -0
- package/templates/base/resources/parameters/test/temporal.yaml.ejs +0 -3
- package/templates/base/root/AGENTS.md.ejs +1 -1
- package/templates/crud/EndpointsController.java.ejs +1 -1
- package/templates/crud/ScaffoldCommand.java.ejs +5 -2
- package/templates/crud/ScaffoldCommandHandler.java.ejs +3 -1
- package/templates/crud/ScaffoldQuery.java.ejs +5 -2
- package/templates/crud/ScaffoldQueryHandler.java.ejs +3 -1
- package/templates/crud/SubEntityRemoveCommand.java.ejs +1 -1
- package/templates/evaluate/report.html.ejs +1447 -90
- package/templates/kafka-event/KafkaConfigBean.java.ejs +1 -1
- package/templates/kafka-event/KafkaMessageBroker.java.ejs +3 -3
- package/templates/ports/PortAclMapper.java.ejs +35 -0
- package/templates/ports/PortFeignAdapter.java.ejs +7 -22
- package/templates/ports/PortFeignClient.java.ejs +4 -0
- package/templates/ports/PortResponseDto.java.ejs +1 -1
- package/templates/rabbitmq-event/RabbitConfigBean.java.ejs +33 -0
- package/templates/rabbitmq-event/RabbitConfigExchange.java.ejs +12 -0
- package/templates/rabbitmq-event/RabbitMessageBroker.java.ejs +35 -0
- package/templates/rabbitmq-event/RabbitMessageBrokerMethod.java.ejs +9 -0
- package/templates/rabbitmq-listener/RabbitConfigConsumerBean.java.ejs +33 -0
- package/templates/rabbitmq-listener/RabbitConfigConsumerExchange.java.ejs +12 -0
- package/templates/rabbitmq-listener/RabbitListenerClass.java.ejs +82 -0
- package/templates/rabbitmq-listener/RabbitListenerSimple.java.ejs +56 -0
- package/templates/read-model/ReadModelJpa.java.ejs +1 -1
- package/templates/read-model/ReadModelJpaRepository.java.ejs +2 -0
- package/templates/read-model/ReadModelRabbitListener.java.ejs +71 -0
- package/templates/read-model/ReadModelRepositoryImpl.java.ejs +9 -5
- package/templates/read-model/ReadModelSyncHandler.java.ejs +2 -0
- package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +18 -4
- package/templates/shared/configurations/rabbitmqConfig/RabbitMQConfig.java.ejs +100 -0
- package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +2 -64
- package/templates/shared/configurations/temporalConfig/TemporalWorkerFactoryLifecycle.java.ejs +41 -0
- package/templates/temporal-activity/ActivityImpl.java.ejs +68 -2
- package/templates/temporal-activity/ActivityInput.java.ejs +14 -0
- package/templates/temporal-activity/ActivityInterface.java.ejs +7 -1
- package/templates/temporal-activity/ActivityOutput.java.ejs +14 -0
- package/templates/temporal-activity/NestedType.java.ejs +12 -0
- package/templates/temporal-activity/SharedActivityInput.java.ejs +14 -0
- package/templates/temporal-activity/SharedActivityInterface.java.ejs +15 -0
- package/templates/temporal-activity/SharedActivityOutput.java.ejs +14 -0
- package/templates/temporal-activity/SharedNestedType.java.ejs +12 -0
- package/templates/temporal-flow/ModuleHeavyActivity.java.ejs +6 -0
- package/templates/temporal-flow/ModuleLightActivity.java.ejs +6 -0
- package/templates/temporal-flow/ModuleTemporalWorkerConfig.java.ejs +58 -0
- package/templates/temporal-flow/WorkFlowImpl.java.ejs +172 -12
- package/templates/temporal-flow/WorkFlowInput.java.ejs +11 -0
- package/templates/temporal-flow/WorkFlowInterface.java.ejs +5 -4
- package/templates/temporal-flow/WorkFlowService.java.ejs +42 -12
- package/COMMAND_EVALUATION.md +0 -911
- package/test-c2010.js +0 -49
- package/test-update-compat.js +0 -109
- package/test-update-lifecycle.js +0 -121
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# Command `generate rabbitmq-event` (alias: `g rabbitmq-event`)
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Generates the infrastructure for publishing a domain event to a RabbitMQ exchange, enabling asynchronous event-driven communication between modules or microservices using topic exchanges, routing keys, and dead-letter queues.
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Implement event publishing without coupling the sender to the consumer. The generated `MessageBroker` port keeps the domain free of RabbitMQ dependencies, while the adapter handles the actual publishing using `EventEnvelope` for correlation tracking. Each event gets its own exchange, queue, routing key, and DLQ configuration.
|
|
10
|
+
|
|
11
|
+
## Syntax
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
eva generate rabbitmq-event <module> [event-name]
|
|
15
|
+
eva g rabbitmq-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; `IntegrationEvent` suffix is added automatically |
|
|
24
|
+
|
|
25
|
+
> **Interactive prompts (when event-name is omitted):**
|
|
26
|
+
> - If `domain.yaml` exists with declared events, a **multi-select list** is shown (including an "All events" option and a "Custom name" fallback).
|
|
27
|
+
> - If no `domain.yaml`, a free-text prompt asks for the event name.
|
|
28
|
+
|
|
29
|
+
**Note:** Running the command multiple times on the same module safely **appends** a new method to the existing `MessageBroker.java` without overwriting it.
|
|
30
|
+
|
|
31
|
+
## Prerequisites
|
|
32
|
+
|
|
33
|
+
RabbitMQ client must be installed in the project:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
eva add rabbitmq-client
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Examples
|
|
40
|
+
|
|
41
|
+
### Example 1: User created event
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
eva g rabbitmq-event user user-created
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Generates:
|
|
48
|
+
- `user/application/events/UserCreatedIntegrationEvent.java`
|
|
49
|
+
- `user/application/ports/MessageBroker.java` (created or appended)
|
|
50
|
+
- `user/infrastructure/adapters/rabbitmqMessageBroker/UserRabbitMessageBroker.java`
|
|
51
|
+
- `shared/configurations/rabbitmqConfig/RabbitMQConfig.java` (exchange + queue + binding beans added)
|
|
52
|
+
- Adds exchange, queue, and routing-key entries to `parameters/*/rabbitmq.yaml`
|
|
53
|
+
|
|
54
|
+
### Example 2: Order placed event
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
eva g rabbitmq-event order order-placed
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Example 3: Multiple events in the same module
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
eva g rabbitmq-event user user-created # Creates MessageBroker with publishUserCreatedIntegrationEvent
|
|
64
|
+
eva g rabbitmq-event user user-updated # Appends publishUserUpdatedIntegrationEvent to MessageBroker
|
|
65
|
+
eva g rabbitmq-event user user-deleted # Appends publishUserDeletedIntegrationEvent to MessageBroker
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Each run adds a new method without overwriting existing ones.
|
|
69
|
+
|
|
70
|
+
### Example 4: Batch mode from domain.yaml
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
eva g rabbitmq-event order
|
|
74
|
+
# Interactive multi-select:
|
|
75
|
+
# ★ All events
|
|
76
|
+
# ──────────────
|
|
77
|
+
# ◻ OrderPlaced (from Order aggregate)
|
|
78
|
+
# ◻ OrderConfirmed (from Order aggregate)
|
|
79
|
+
# ◻ OrderCancelled (from Order aggregate)
|
|
80
|
+
# ──────────────
|
|
81
|
+
# ◻ Custom name (free text)...
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Select "All events" to generate all declared domain events at once.
|
|
85
|
+
|
|
86
|
+
## Generated Code Structure
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
<module>/
|
|
90
|
+
├── application/
|
|
91
|
+
│ ├── events/
|
|
92
|
+
│ │ └── UserCreatedIntegrationEvent.java # Java record (event data)
|
|
93
|
+
│ └── ports/
|
|
94
|
+
│ └── MessageBroker.java # Port interface (append-safe)
|
|
95
|
+
│
|
|
96
|
+
└── infrastructure/
|
|
97
|
+
└── adapters/
|
|
98
|
+
└── rabbitmqMessageBroker/
|
|
99
|
+
└── UserRabbitMessageBroker.java # RabbitMQ adapter
|
|
100
|
+
|
|
101
|
+
shared/
|
|
102
|
+
└── infrastructure/
|
|
103
|
+
└── configurations/
|
|
104
|
+
└── rabbitmqConfig/
|
|
105
|
+
└── RabbitMQConfig.java # Exchange + Queue + Binding beans
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Generated Files
|
|
109
|
+
|
|
110
|
+
### 1. Integration Event (Application Layer)
|
|
111
|
+
|
|
112
|
+
**UserCreatedIntegrationEvent.java** (`application/events/`):
|
|
113
|
+
```java
|
|
114
|
+
package com.example.project.user.application.events;
|
|
115
|
+
|
|
116
|
+
public record UserCreatedIntegrationEvent(
|
|
117
|
+
// TODO: Add your event fields here
|
|
118
|
+
// Example:
|
|
119
|
+
// Long id,
|
|
120
|
+
// String name,
|
|
121
|
+
// LocalDateTime createdAt
|
|
122
|
+
) {
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
> Events are pure Java records — immutable, no Lombok, no framework annotations.
|
|
127
|
+
|
|
128
|
+
### 2. MessageBroker Port (Application Layer)
|
|
129
|
+
|
|
130
|
+
**MessageBroker.java** (`application/ports/`):
|
|
131
|
+
```java
|
|
132
|
+
package com.example.project.user.application.ports;
|
|
133
|
+
|
|
134
|
+
import com.example.project.user.application.events.UserCreatedIntegrationEvent;
|
|
135
|
+
|
|
136
|
+
public interface MessageBroker {
|
|
137
|
+
|
|
138
|
+
void publishUserCreatedIntegrationEvent(UserCreatedIntegrationEvent event);
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
> If `MessageBroker.java` already exists (from a previous run), the new `publishXxx` method is **appended** to the interface rather than overwriting it.
|
|
143
|
+
|
|
144
|
+
### 3. RabbitMQ Adapter (Infrastructure)
|
|
145
|
+
|
|
146
|
+
**UserRabbitMessageBroker.java** (`infrastructure/adapters/rabbitmqMessageBroker/`):
|
|
147
|
+
```java
|
|
148
|
+
package com.example.project.user.infrastructure.adapters.rabbitmqMessageBroker;
|
|
149
|
+
|
|
150
|
+
import com.example.project.user.application.events.UserCreatedIntegrationEvent;
|
|
151
|
+
import com.example.project.user.application.ports.MessageBroker;
|
|
152
|
+
import com.example.project.shared.infrastructure.eventEnvelope.EventEnvelope;
|
|
153
|
+
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
|
154
|
+
import org.springframework.beans.factory.annotation.Value;
|
|
155
|
+
import org.springframework.stereotype.Component;
|
|
156
|
+
import org.slf4j.MDC;
|
|
157
|
+
|
|
158
|
+
@Component("userRabbitMessageBroker")
|
|
159
|
+
public class UserRabbitMessageBroker implements MessageBroker {
|
|
160
|
+
|
|
161
|
+
@Value("${exchanges.user}")
|
|
162
|
+
private String exchange;
|
|
163
|
+
|
|
164
|
+
@Value("${routing-keys.user-created}")
|
|
165
|
+
private String userCreatedRoutingKey;
|
|
166
|
+
|
|
167
|
+
private final RabbitTemplate rabbitTemplate;
|
|
168
|
+
|
|
169
|
+
public UserRabbitMessageBroker(RabbitTemplate rabbitTemplate) {
|
|
170
|
+
this.rabbitTemplate = rabbitTemplate;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@Override
|
|
174
|
+
public void publishUserCreatedIntegrationEvent(UserCreatedIntegrationEvent event) {
|
|
175
|
+
EventEnvelope<UserCreatedIntegrationEvent> envelope = EventEnvelope.of(
|
|
176
|
+
userCreatedRoutingKey,
|
|
177
|
+
event,
|
|
178
|
+
MDC.get("correlationId")
|
|
179
|
+
);
|
|
180
|
+
rabbitTemplate.convertAndSend(exchange, userCreatedRoutingKey, envelope);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
> `EventEnvelope.of(routingKey, payload, correlationId)` wraps the event with routing metadata and the current MDC correlation ID for distributed tracing.
|
|
186
|
+
|
|
187
|
+
### 4. RabbitMQConfig Beans (Shared)
|
|
188
|
+
|
|
189
|
+
For each event, the command adds exchange, queue, binding, and DLQ beans to `RabbitMQConfig.java`:
|
|
190
|
+
|
|
191
|
+
```java
|
|
192
|
+
// ── Exchange (once per module) ──────────────────────────
|
|
193
|
+
|
|
194
|
+
@Value("${exchanges.user}")
|
|
195
|
+
private String userExchangeName;
|
|
196
|
+
|
|
197
|
+
@Bean
|
|
198
|
+
public TopicExchange userExchange() {
|
|
199
|
+
return new TopicExchange(userExchangeName, true, false);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
@Bean
|
|
203
|
+
public TopicExchange userDlxExchange() {
|
|
204
|
+
return new TopicExchange(userExchangeName + ".dlx", true, false);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ── Queue + Binding (per event) ─────────────────────────
|
|
208
|
+
|
|
209
|
+
@Value("${queues.user-created}")
|
|
210
|
+
private String userCreatedTopicQueueName;
|
|
211
|
+
|
|
212
|
+
@Value("${routing-keys.user-created}")
|
|
213
|
+
private String userCreatedTopicRoutingKeyValue;
|
|
214
|
+
|
|
215
|
+
@Bean
|
|
216
|
+
public Queue userCreatedTopicQueue() {
|
|
217
|
+
return QueueBuilder.durable(userCreatedTopicQueueName)
|
|
218
|
+
.withArgument("x-dead-letter-exchange", userExchangeName + ".dlx")
|
|
219
|
+
.build();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@Bean
|
|
223
|
+
public Binding userCreatedTopicBinding() {
|
|
224
|
+
return BindingBuilder
|
|
225
|
+
.bind(userCreatedTopicQueue())
|
|
226
|
+
.to(userExchange())
|
|
227
|
+
.with(userCreatedTopicRoutingKeyValue);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@Bean
|
|
231
|
+
public Queue userCreatedTopicDlq() {
|
|
232
|
+
return QueueBuilder.durable(userCreatedTopicQueueName + ".dlq").build();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@Bean
|
|
236
|
+
public Binding userCreatedTopicDlqBinding() {
|
|
237
|
+
return BindingBuilder
|
|
238
|
+
.bind(userCreatedTopicDlq())
|
|
239
|
+
.to(userDlxExchange())
|
|
240
|
+
.with(userCreatedTopicRoutingKeyValue);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Configuration Added
|
|
245
|
+
|
|
246
|
+
```yaml
|
|
247
|
+
# parameters/local/rabbitmq.yaml
|
|
248
|
+
exchanges:
|
|
249
|
+
user: user.events
|
|
250
|
+
|
|
251
|
+
queues:
|
|
252
|
+
user-created: user.user-created
|
|
253
|
+
|
|
254
|
+
routing-keys:
|
|
255
|
+
user-created: user.created
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The same entries are added to every environment (`local`, `develop`, `test`, `production`).
|
|
259
|
+
|
|
260
|
+
### RabbitMQ Topology
|
|
261
|
+
|
|
262
|
+
Each event generates the following topology:
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
Producer → [user.events exchange] → routing key: user.created → [user.user-created queue]
|
|
266
|
+
↓ (on failure)
|
|
267
|
+
[user.events.dlx exchange] → routing key: user.created → [user.user-created.dlq queue]
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Usage in a Command Handler
|
|
271
|
+
|
|
272
|
+
Inject `MessageBroker` (the port, not the RabbitMQ class) and call the publish method:
|
|
273
|
+
|
|
274
|
+
```java
|
|
275
|
+
@ApplicationComponent
|
|
276
|
+
public class CreateUserCommandHandler {
|
|
277
|
+
|
|
278
|
+
private final UserRepository userRepository;
|
|
279
|
+
private final MessageBroker messageBroker;
|
|
280
|
+
|
|
281
|
+
public CreateUserCommandHandler(UserRepository userRepository, MessageBroker messageBroker) {
|
|
282
|
+
this.userRepository = userRepository;
|
|
283
|
+
this.messageBroker = messageBroker;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
public void handle(CreateUserCommand command) {
|
|
287
|
+
User user = new User(command.name(), command.email());
|
|
288
|
+
userRepository.save(user);
|
|
289
|
+
|
|
290
|
+
messageBroker.publishUserCreatedIntegrationEvent(
|
|
291
|
+
new UserCreatedIntegrationEvent(/* fill fields */)
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## DomainEventHandler Wiring
|
|
298
|
+
|
|
299
|
+
When the module has domain events (from `domain.yaml`), the command also wires the `DomainEventHandler` to publish integration events after transaction commit:
|
|
300
|
+
|
|
301
|
+
```java
|
|
302
|
+
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
|
303
|
+
public void onUserCreated(UserCreated event) {
|
|
304
|
+
messageBroker.publishUserCreatedIntegrationEvent(
|
|
305
|
+
new UserCreatedIntegrationEvent(event.getUserId(), event.getCreatedAt())
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Bean Naming
|
|
311
|
+
|
|
312
|
+
The adapter is registered with an explicit bean name to avoid conflicts when multiple modules have a `MessageBroker`:
|
|
313
|
+
|
|
314
|
+
| Module | Bean name |
|
|
315
|
+
|--------|-----------|
|
|
316
|
+
| `user` | `userRabbitMessageBroker` |
|
|
317
|
+
| `order` | `orderRabbitMessageBroker` |
|
|
318
|
+
| `product` | `productRabbitMessageBroker` |
|
|
319
|
+
|
|
320
|
+
Spring resolves the correct adapter for each module via constructor injection by type + qualifier if needed.
|
|
321
|
+
|
|
322
|
+
## Comparison with Kafka Events
|
|
323
|
+
|
|
324
|
+
| Aspect | Kafka | RabbitMQ |
|
|
325
|
+
|--------|-------|----------|
|
|
326
|
+
| Adapter class | `{Module}KafkaMessageBroker` | `{Module}RabbitMessageBroker` |
|
|
327
|
+
| Adapter directory | `kafkaMessageBroker/` | `rabbitmqMessageBroker/` |
|
|
328
|
+
| Config file | `kafka.yaml` | `rabbitmq.yaml` |
|
|
329
|
+
| Routing | Topic name | Exchange + routing key |
|
|
330
|
+
| DLQ | Configured externally | Auto-generated DLQ + DLX per event |
|
|
331
|
+
| Config class | `KafkaConfig.java` (NewTopic beans) | `RabbitMQConfig.java` (Exchange + Queue + Binding beans) |
|
|
332
|
+
| Broker-agnostic port | `MessageBroker.java` (identical) | `MessageBroker.java` (identical) |
|
|
333
|
+
|
|
334
|
+
> **Mutual exclusivity:** Only one broker is allowed per project (`eva add kafka-client` or `eva add rabbitmq-client`, not both).
|
|
335
|
+
|
|
336
|
+
## See Also
|
|
337
|
+
|
|
338
|
+
- [generate rabbitmq-listener](./GENERATE_RABBITMQ_LISTENER.md) — Create RabbitMQ event consumer
|
|
339
|
+
- [generate kafka-event](./GENERATE_KAFKA_EVENT.md) — Kafka equivalent of this command
|
|
340
|
+
- [generate usecase](./GENERATE_USECASE.md) — Create use case handlers
|
|
341
|
+
- [RabbitMQ Production Config](../RABBITMQ_PRODUCTION_CONFIG.md) — Production-ready configuration reference
|