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.
- package/AGENTS.md +441 -14
- package/DOMAIN_YAML_GUIDE.md +425 -21
- package/FUTURE_FEATURES.md +315 -115
- package/QUICK_REFERENCE.md +101 -153
- package/README.md +77 -70
- package/bin/eva4j.js +57 -1
- package/config/defaults.json +3 -0
- package/docs/commands/GENERATE_ENTITIES.md +662 -1968
- package/docs/commands/GENERATE_HTTP_EXCHANGE.md +274 -450
- package/docs/commands/GENERATE_KAFKA_EVENT.md +219 -498
- package/docs/commands/GENERATE_KAFKA_LISTENER.md +18 -18
- package/docs/commands/GENERATE_RECORD.md +335 -311
- package/docs/commands/GENERATE_TEMPORAL_ACTIVITY.md +174 -0
- package/docs/commands/GENERATE_TEMPORAL_FLOW.md +237 -0
- package/docs/commands/GENERATE_USECASE.md +216 -282
- package/docs/commands/INDEX.md +36 -7
- package/examples/doctor-evaluation.yaml +3 -3
- package/examples/domain-audit-complete.yaml +2 -2
- package/examples/domain-collections.yaml +2 -2
- package/examples/domain-ecommerce.yaml +2 -2
- package/examples/domain-events.yaml +201 -0
- package/examples/domain-field-visibility.yaml +11 -5
- package/examples/domain-multi-aggregate.yaml +12 -6
- package/examples/domain-one-to-many.yaml +1 -1
- package/examples/domain-one-to-one.yaml +1 -1
- package/examples/domain-secondary-onetomany.yaml +1 -1
- package/examples/domain-secondary-onetoone.yaml +1 -1
- package/examples/domain-simple.yaml +1 -1
- package/examples/domain-soft-delete.yaml +3 -3
- package/examples/domain-transitions.yaml +1 -1
- package/examples/domain-value-objects.yaml +1 -1
- package/package.json +2 -2
- package/src/commands/add-kafka-client.js +3 -1
- package/src/commands/add-temporal-client.js +286 -0
- package/src/commands/generate-entities.js +75 -4
- package/src/commands/generate-kafka-event.js +273 -89
- package/src/commands/generate-temporal-activity.js +228 -0
- package/src/commands/generate-temporal-flow.js +216 -0
- package/src/generators/module-generator.js +1 -0
- package/src/generators/shared-generator.js +26 -0
- package/src/utils/yaml-to-entity.js +93 -4
- package/templates/aggregate/AggregateRepository.java.ejs +3 -2
- package/templates/aggregate/AggregateRepositoryImpl.java.ejs +15 -7
- package/templates/aggregate/AggregateRoot.java.ejs +38 -2
- package/templates/aggregate/DomainEntity.java.ejs +6 -2
- package/templates/aggregate/DomainEventHandler.java.ejs +62 -0
- package/templates/aggregate/DomainEventRecord.java.ejs +50 -0
- package/templates/aggregate/JpaAggregateRoot.java.ejs +3 -1
- package/templates/aggregate/JpaEntity.java.ejs +3 -1
- package/templates/base/docker/kafka-services.yaml.ejs +2 -2
- package/templates/base/docker/temporal-services.yaml.ejs +29 -0
- package/templates/base/resources/parameters/develop/temporal.yaml.ejs +9 -0
- package/templates/base/resources/parameters/local/temporal.yaml.ejs +9 -0
- package/templates/base/resources/parameters/production/temporal.yaml.ejs +9 -0
- package/templates/base/resources/parameters/test/temporal.yaml.ejs +9 -0
- package/templates/base/root/AGENTS.md.ejs +916 -51
- package/templates/crud/Controller.java.ejs +36 -6
- package/templates/crud/ListQuery.java.ejs +6 -2
- package/templates/crud/ListQueryHandler.java.ejs +24 -10
- package/templates/crud/UpdateCommand.java.ejs +52 -0
- package/templates/crud/UpdateCommandHandler.java.ejs +105 -0
- package/templates/kafka-event/DomainEventHandlerMethod.ejs +1 -0
- package/templates/kafka-event/Event.java.ejs +23 -0
- package/templates/shared/application/dtos/PagedResponse.java.ejs +30 -0
- package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +104 -0
- package/templates/shared/domain/DomainEvent.java.ejs +40 -0
- package/templates/shared/interfaces/HeavyActivity.java.ejs +4 -0
- package/templates/shared/interfaces/LightActivity.java.ejs +4 -0
- package/templates/temporal-activity/ActivityImpl.java.ejs +14 -0
- package/templates/temporal-activity/ActivityInterface.java.ejs +11 -0
- package/templates/temporal-flow/WorkFlowImpl.java.ejs +64 -0
- package/templates/temporal-flow/WorkFlowInterface.java.ejs +19 -0
- package/templates/temporal-flow/WorkFlowService.java.ejs +49 -0
|
@@ -1,450 +1,274 @@
|
|
|
1
|
-
# Command `generate http-exchange` (alias: `g http`)
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
Generates HTTP client
|
|
6
|
-
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
Enable modules to communicate with external HTTP services
|
|
10
|
-
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### Parameters
|
|
19
|
-
|
|
20
|
-
| Parameter | Required | Description |
|
|
21
|
-
|-----------|----------|-------------|
|
|
22
|
-
| `
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
#
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
### 4. Third-Party Service
|
|
276
|
-
|
|
277
|
-
```java
|
|
278
|
-
@FeignClient(name = "sendgrid", url = "${external.sendgrid.url}")
|
|
279
|
-
public interface SendGridClient {
|
|
280
|
-
|
|
281
|
-
@PostMapping("/v3/mail/send")
|
|
282
|
-
void sendEmail(
|
|
283
|
-
@RequestHeader("Authorization") String apiKey,
|
|
284
|
-
@RequestBody EmailRequest email
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
## 🛡️ Error Handling
|
|
290
|
-
|
|
291
|
-
### Custom Error Decoder
|
|
292
|
-
|
|
293
|
-
```java
|
|
294
|
-
@Configuration
|
|
295
|
-
public class PaymentGatewayConfig {
|
|
296
|
-
|
|
297
|
-
@Bean
|
|
298
|
-
public ErrorDecoder errorDecoder() {
|
|
299
|
-
return (methodKey, response) -> {
|
|
300
|
-
if (response.status() >= 400 && response.status() <= 499) {
|
|
301
|
-
return new PaymentClientException("Client error: " + response.reason());
|
|
302
|
-
}
|
|
303
|
-
if (response.status() >= 500 && response.status() <= 599) {
|
|
304
|
-
return new PaymentServerException("Server error: " + response.reason());
|
|
305
|
-
}
|
|
306
|
-
return new Exception("Generic error");
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### Fallback Support
|
|
313
|
-
|
|
314
|
-
```java
|
|
315
|
-
@FeignClient(
|
|
316
|
-
name = "payment-gateway",
|
|
317
|
-
url = "${external.payment-gateway.url}",
|
|
318
|
-
fallback = PaymentGatewayFallback.class
|
|
319
|
-
)
|
|
320
|
-
public interface PaymentGatewayClient {
|
|
321
|
-
@PostMapping("/payments")
|
|
322
|
-
PaymentResponse processPayment(@RequestBody PaymentRequest request);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
@Component
|
|
326
|
-
public class PaymentGatewayFallback implements PaymentGatewayClient {
|
|
327
|
-
@Override
|
|
328
|
-
public PaymentResponse processPayment(PaymentRequest request) {
|
|
329
|
-
// Return fallback response
|
|
330
|
-
return PaymentResponse.error("Service temporarily unavailable");
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
## 🚀 Next Steps
|
|
336
|
-
|
|
337
|
-
After generating the HTTP exchange:
|
|
338
|
-
|
|
339
|
-
1. **Define the contract in the Port:**
|
|
340
|
-
```java
|
|
341
|
-
public interface PaymentGatewayPort {
|
|
342
|
-
PaymentResponse processPayment(PaymentRequest request);
|
|
343
|
-
PaymentStatus checkStatus(String transactionId);
|
|
344
|
-
}
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
2. **Implement Feign client endpoints:**
|
|
348
|
-
```java
|
|
349
|
-
@FeignClient(name = "payment-gateway", url = "${external.payment.url}")
|
|
350
|
-
public interface PaymentGatewayClient {
|
|
351
|
-
@PostMapping("/v1/payments")
|
|
352
|
-
PaymentResponse processPayment(@RequestBody PaymentRequest request);
|
|
353
|
-
|
|
354
|
-
@GetMapping("/v1/payments/{id}/status")
|
|
355
|
-
PaymentStatus checkStatus(@PathVariable String id);
|
|
356
|
-
}
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
3. **Implement the Adapter:**
|
|
360
|
-
```java
|
|
361
|
-
@Service
|
|
362
|
-
@RequiredArgsConstructor
|
|
363
|
-
public class PaymentGatewayAdapter implements PaymentGatewayPort {
|
|
364
|
-
private final PaymentGatewayClient client;
|
|
365
|
-
|
|
366
|
-
@Override
|
|
367
|
-
public PaymentResponse processPayment(PaymentRequest request) {
|
|
368
|
-
return client.processPayment(request);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
@Override
|
|
372
|
-
public PaymentStatus checkStatus(String transactionId) {
|
|
373
|
-
return client.checkStatus(transactionId);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
4. **Configure the service URL:**
|
|
379
|
-
```yaml
|
|
380
|
-
external:
|
|
381
|
-
payment:
|
|
382
|
-
url: https://api.payment-provider.com
|
|
383
|
-
api-key: ${PAYMENT_API_KEY}
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
5. **Use the port in your domain:**
|
|
387
|
-
```java
|
|
388
|
-
@Service
|
|
389
|
-
@RequiredArgsConstructor
|
|
390
|
-
public class ProcessOrderCommandHandler {
|
|
391
|
-
private final PaymentGatewayPort paymentGateway;
|
|
392
|
-
|
|
393
|
-
public void handle(ProcessOrderCommand command) {
|
|
394
|
-
PaymentResponse payment = paymentGateway.processPayment(
|
|
395
|
-
new PaymentRequest(command.getAmount())
|
|
396
|
-
);
|
|
397
|
-
// Continue with business logic
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
## ⚠️ Prerequisites
|
|
403
|
-
|
|
404
|
-
- Be in a project created with `eva4j create`
|
|
405
|
-
- Module must exist
|
|
406
|
-
- Spring Cloud OpenFeign dependency (automatically added)
|
|
407
|
-
|
|
408
|
-
## 🔍 Validations
|
|
409
|
-
|
|
410
|
-
The command validates:
|
|
411
|
-
- ✅ Valid eva4j project
|
|
412
|
-
- ✅ Client name is in PascalCase
|
|
413
|
-
- ✅ Module exists
|
|
414
|
-
- ✅ Feign is configured in the project
|
|
415
|
-
|
|
416
|
-
## 📚 See Also
|
|
417
|
-
|
|
418
|
-
- [generate-kafka-event](./GENERATE_KAFKA_EVENT.md) - Async communication
|
|
419
|
-
- [add-module](./ADD_MODULE.md) - Create modules
|
|
420
|
-
- [detach](./DETACH.md) - Extract to microservices
|
|
421
|
-
|
|
422
|
-
## 🐛 Troubleshooting
|
|
423
|
-
|
|
424
|
-
**Error: "Feign client not found"**
|
|
425
|
-
- Solution: Ensure `@EnableFeignClients` is in your main Application class
|
|
426
|
-
|
|
427
|
-
**Connection timeout errors**
|
|
428
|
-
- Solution: Increase timeout in configuration:
|
|
429
|
-
```yaml
|
|
430
|
-
feign:
|
|
431
|
-
client:
|
|
432
|
-
config:
|
|
433
|
-
default:
|
|
434
|
-
connectTimeout: 10000
|
|
435
|
-
readTimeout: 20000
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
**401/403 authentication errors**
|
|
439
|
-
- Solution: Add authentication in RequestInterceptor
|
|
440
|
-
```java
|
|
441
|
-
@Bean
|
|
442
|
-
public RequestInterceptor authInterceptor() {
|
|
443
|
-
return template -> {
|
|
444
|
-
template.header("Authorization", "Bearer " + getToken());
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
**Load balancer errors**
|
|
450
|
-
- Solution: Specify URL directly or configure service discovery
|
|
1
|
+
# Command `generate http-exchange` (alias: `g http-exchange`)
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Generates an HTTP client adapter using Spring Cloud OpenFeign for consuming external REST APIs, following hexagonal architecture.
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Enable modules to communicate with external HTTP services through a clean, declarative interface while maintaining architectural boundaries (Port → Adapter → FeignClient).
|
|
10
|
+
|
|
11
|
+
## Syntax
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
eva generate http-exchange <module> [port-name]
|
|
15
|
+
eva g http-exchange <module> [port-name] # Short alias
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Parameters
|
|
19
|
+
|
|
20
|
+
| Parameter | Required | Description |
|
|
21
|
+
|-----------|----------|-------------|
|
|
22
|
+
| `module` | Yes | Module that will own the HTTP client (e.g., `order`, `payment`) |
|
|
23
|
+
| `port-name` | No | Name of the external service in PascalCase — prompted if omitted |
|
|
24
|
+
|
|
25
|
+
> **Interactive prompts:**
|
|
26
|
+
> 1. **Port name** — if not provided (e.g., `PaymentGateway`, `ProductService`)
|
|
27
|
+
> 2. **Base URL** — default URL of the remote service for local environment
|
|
28
|
+
|
|
29
|
+
## Examples
|
|
30
|
+
|
|
31
|
+
### Example 1: Payment gateway in the order module
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
eva g http-exchange order payment-gateway
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Generates:
|
|
38
|
+
- `order/application/ports/PaymentGateway.java`
|
|
39
|
+
- `order/infrastructure/adapters/paymentGateway/PaymentGatewayAdapter.java`
|
|
40
|
+
- `order/infrastructure/adapters/paymentGateway/PaymentGatewayFeignClient.java`
|
|
41
|
+
- `order/infrastructure/adapters/paymentGateway/PaymentGatewayConfig.java`
|
|
42
|
+
- Adds entry to `parameters/*/urls.yaml`
|
|
43
|
+
|
|
44
|
+
### Example 2: User service client
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
eva g http-exchange order user-service
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Example 3: Inventory service
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
eva g http-exchange product inventory-service
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Generated Code Structure
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
<module>/
|
|
60
|
+
├── application/
|
|
61
|
+
│ └── ports/
|
|
62
|
+
│ └── PaymentGateway.java # Port interface
|
|
63
|
+
│
|
|
64
|
+
└── infrastructure/
|
|
65
|
+
└── adapters/
|
|
66
|
+
└── paymentGateway/
|
|
67
|
+
├── PaymentGatewayAdapter.java # Adapter (@Component)
|
|
68
|
+
├── PaymentGatewayFeignClient.java # Feign client
|
|
69
|
+
└── PaymentGatewayConfig.java # Feign config (timeouts)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Generated Files
|
|
73
|
+
|
|
74
|
+
### 1. Port (Application Layer)
|
|
75
|
+
|
|
76
|
+
**PaymentGateway.java** (`application/ports/`):
|
|
77
|
+
```java
|
|
78
|
+
package com.example.project.order.application.ports;
|
|
79
|
+
|
|
80
|
+
public interface PaymentGateway {
|
|
81
|
+
|
|
82
|
+
Object findAll();
|
|
83
|
+
|
|
84
|
+
Object findById(Long id);
|
|
85
|
+
|
|
86
|
+
Object create(Object request);
|
|
87
|
+
|
|
88
|
+
Object update(Long id, Object request);
|
|
89
|
+
|
|
90
|
+
void delete(Long id);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> The port exposes generic `Object` methods as scaffolding. Replace them with typed methods that match the remote API contract.
|
|
95
|
+
|
|
96
|
+
### 2. Feign Client (Infrastructure)
|
|
97
|
+
|
|
98
|
+
**PaymentGatewayFeignClient.java** (`infrastructure/adapters/paymentGateway/`):
|
|
99
|
+
```java
|
|
100
|
+
package com.example.project.order.infrastructure.adapters.paymentGateway;
|
|
101
|
+
|
|
102
|
+
import org.springframework.cloud.openfeign.FeignClient;
|
|
103
|
+
import org.springframework.web.bind.annotation.*;
|
|
104
|
+
|
|
105
|
+
@FeignClient(
|
|
106
|
+
name = "order-payment-gateway",
|
|
107
|
+
url = "${order.payment-gateway.base-url}",
|
|
108
|
+
configuration = PaymentGatewayConfig.class
|
|
109
|
+
)
|
|
110
|
+
public interface PaymentGatewayFeignClient {
|
|
111
|
+
|
|
112
|
+
@GetMapping("/api/resources")
|
|
113
|
+
Object findAll();
|
|
114
|
+
|
|
115
|
+
@GetMapping("/api/resources/{id}")
|
|
116
|
+
Object findById(@PathVariable("id") Long id);
|
|
117
|
+
|
|
118
|
+
@PostMapping("/api/resources")
|
|
119
|
+
Object create(@RequestBody Object request);
|
|
120
|
+
|
|
121
|
+
@PutMapping("/api/resources/{id}")
|
|
122
|
+
Object update(@PathVariable("id") Long id, @RequestBody Object request);
|
|
123
|
+
|
|
124
|
+
@DeleteMapping("/api/resources/{id}")
|
|
125
|
+
void delete(@PathVariable("id") Long id);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
> Property key format: `<module-kebab>.<port-kebab>.base-url`
|
|
130
|
+
|
|
131
|
+
### 3. Config (Infrastructure)
|
|
132
|
+
|
|
133
|
+
**PaymentGatewayConfig.java** (`infrastructure/adapters/paymentGateway/`):
|
|
134
|
+
```java
|
|
135
|
+
package com.example.project.order.infrastructure.adapters.paymentGateway;
|
|
136
|
+
|
|
137
|
+
import feign.Logger;
|
|
138
|
+
import feign.Request;
|
|
139
|
+
import org.springframework.context.annotation.Bean;
|
|
140
|
+
|
|
141
|
+
import java.util.concurrent.TimeUnit;
|
|
142
|
+
|
|
143
|
+
public class PaymentGatewayConfig {
|
|
144
|
+
|
|
145
|
+
@Bean
|
|
146
|
+
public Logger.Level feignLoggerLevel() {
|
|
147
|
+
return Logger.Level.BASIC;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@Bean
|
|
151
|
+
public Request.Options feignOptions() {
|
|
152
|
+
return new Request.Options(
|
|
153
|
+
15, TimeUnit.SECONDS, // connect timeout
|
|
154
|
+
15, TimeUnit.SECONDS, // read timeout
|
|
155
|
+
true
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
> **No `@Configuration` annotation** — the class is referenced directly via `configuration = PaymentGatewayConfig.class` in the `@FeignClient`, which is the standard OpenFeign pattern.
|
|
162
|
+
|
|
163
|
+
### 4. Adapter (Infrastructure)
|
|
164
|
+
|
|
165
|
+
**PaymentGatewayAdapter.java** (`infrastructure/adapters/paymentGateway/`):
|
|
166
|
+
```java
|
|
167
|
+
package com.example.project.order.infrastructure.adapters.paymentGateway;
|
|
168
|
+
|
|
169
|
+
import com.example.project.order.application.ports.PaymentGateway;
|
|
170
|
+
import org.springframework.stereotype.Component;
|
|
171
|
+
|
|
172
|
+
@Component
|
|
173
|
+
public class PaymentGatewayAdapter implements PaymentGateway {
|
|
174
|
+
|
|
175
|
+
private final PaymentGatewayFeignClient feignClient;
|
|
176
|
+
|
|
177
|
+
public PaymentGatewayAdapter(PaymentGatewayFeignClient feignClient) {
|
|
178
|
+
this.feignClient = feignClient;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@Override
|
|
182
|
+
public Object findAll() {
|
|
183
|
+
return feignClient.findAll();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@Override
|
|
187
|
+
public Object findById(Long id) {
|
|
188
|
+
return feignClient.findById(id);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@Override
|
|
192
|
+
public Object create(Object request) {
|
|
193
|
+
return feignClient.create(request);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@Override
|
|
197
|
+
public Object update(Long id, Object request) {
|
|
198
|
+
return feignClient.update(id, request);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@Override
|
|
202
|
+
public void delete(Long id) {
|
|
203
|
+
feignClient.delete(id);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Configuration Added
|
|
209
|
+
|
|
210
|
+
The command appends the base URL to every environment's `urls.yaml`:
|
|
211
|
+
|
|
212
|
+
```yaml
|
|
213
|
+
# parameters/local/urls.yaml
|
|
214
|
+
order:
|
|
215
|
+
payment-gateway:
|
|
216
|
+
base-url: http://localhost:8050 # value entered at prompt
|
|
217
|
+
|
|
218
|
+
# parameters/develop/urls.yaml
|
|
219
|
+
order:
|
|
220
|
+
payment-gateway:
|
|
221
|
+
base-url: https://dev-payment.company.com
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Property key pattern: `<module-kebab>.<port-kebab>.base-url`
|
|
225
|
+
|
|
226
|
+
## Usage in Code
|
|
227
|
+
|
|
228
|
+
Inject the Port interface (not the FeignClient directly):
|
|
229
|
+
|
|
230
|
+
```java
|
|
231
|
+
@ApplicationComponent
|
|
232
|
+
public class ProcessPaymentCommandHandler {
|
|
233
|
+
|
|
234
|
+
private final PaymentGateway paymentGateway; // ← Port interface
|
|
235
|
+
|
|
236
|
+
public ProcessPaymentCommandHandler(PaymentGateway paymentGateway) {
|
|
237
|
+
this.paymentGateway = paymentGateway;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
public void handle(ProcessPaymentCommand command) {
|
|
241
|
+
paymentGateway.create(command);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Customization
|
|
247
|
+
|
|
248
|
+
After generation, replace the generic `Object` signatures with typed DTOs:
|
|
249
|
+
|
|
250
|
+
```java
|
|
251
|
+
// Port
|
|
252
|
+
PaymentResponse processPayment(PaymentRequest request);
|
|
253
|
+
|
|
254
|
+
// FeignClient
|
|
255
|
+
@PostMapping("/payments")
|
|
256
|
+
PaymentResponse processPayment(@RequestBody PaymentRequest request);
|
|
257
|
+
|
|
258
|
+
// Adapter
|
|
259
|
+
@Override
|
|
260
|
+
public PaymentResponse processPayment(PaymentRequest request) {
|
|
261
|
+
return feignClient.processPayment(request);
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Prerequisites
|
|
266
|
+
|
|
267
|
+
- Be in a project created with `eva create`
|
|
268
|
+
- Module must exist (`eva add module <module>`)
|
|
269
|
+
- `spring-cloud-openfeign` dependency must be present (included by default in eva projects)
|
|
270
|
+
|
|
271
|
+
## See Also
|
|
272
|
+
|
|
273
|
+
- [generate-kafka-event](./GENERATE_KAFKA_EVENT.md) — Async event publishing
|
|
274
|
+
- [add-module](./ADD_MODULE.md) — Create a new module
|