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,450 +1,274 @@
1
- # Command `generate http-exchange` (alias: `g http`)
2
-
3
- ## 📋 Description
4
-
5
- Generates HTTP client infrastructure using Spring Cloud OpenFeign for consuming external REST APIs, following the hexagonal architecture pattern with ports and adapters.
6
-
7
- ## 🎯 Purpose
8
-
9
- Enable modules to communicate with external HTTP services or other microservices through clean, declarative interfaces while maintaining architectural boundaries.
10
-
11
- ## 📝 Syntax
12
-
13
- ```bash
14
- eva4j generate http-exchange <ClientName>
15
- eva4j g http <ClientName> # Short alias
16
- ```
17
-
18
- ### Parameters
19
-
20
- | Parameter | Required | Description |
21
- |-----------|----------|-------------|
22
- | `ClientName` | Yes | Name of the HTTP client (PascalCase, e.g., PaymentGateway, UserService) |
23
-
24
- ## 💡 Examples
25
-
26
- ### Example 1: Payment Gateway Client
27
-
28
- ```bash
29
- eva4j g http PaymentGateway
30
- ```
31
-
32
- ### Example 2: User Service Client
33
-
34
- ```bash
35
- eva4j g http UserService
36
- ```
37
-
38
- ### Example 3: External API Client
39
-
40
- ```bash
41
- eva4j g http WeatherApi
42
- ```
43
-
44
- ## 📦 Generated Code Structure
45
-
46
- ```
47
- <module>/
48
- ├── domain/
49
- │ └── ports/
50
- │ └── PaymentGatewayPort.java # Port (interface)
51
-
52
- ├── application/
53
- │ └── services/
54
- │ └── PaymentGatewayAdapter.java # Adapter implementation
55
-
56
- └── infrastructure/
57
- └── external/
58
- ├── clients/
59
- │ └── PaymentGatewayClient.java # Feign client
60
- └── config/
61
- └── PaymentGatewayConfig.java # Feign configuration
62
- ```
63
-
64
- ## 📄 Generated Files
65
-
66
- ### 1. Port (Domain Layer)
67
-
68
- **PaymentGatewayPort.java:**
69
- ```java
70
- package com.example.project.payment.domain.ports;
71
-
72
- /**
73
- * Port for PaymentGateway external communication
74
- */
75
- public interface PaymentGatewayPort {
76
-
77
- // Define your methods here
78
- // Example:
79
- // PaymentResponse processPayment(PaymentRequest request);
80
- }
81
- ```
82
-
83
- ### 2. Feign Client (Infrastructure Layer)
84
-
85
- **PaymentGatewayClient.java:**
86
- ```java
87
- package com.example.project.payment.infrastructure.external.clients;
88
-
89
- import org.springframework.cloud.openfeign.FeignClient;
90
- import org.springframework.web.bind.annotation.*;
91
-
92
- /**
93
- * Feign client for PaymentGateway
94
- */
95
- @FeignClient(
96
- name = "payment-gateway",
97
- url = "${external.payment-gateway.url}",
98
- configuration = PaymentGatewayConfig.class
99
- )
100
- public interface PaymentGatewayClient {
101
-
102
- // Define your HTTP endpoints here
103
- // Example:
104
- // @PostMapping("/payments")
105
- // PaymentResponse processPayment(@RequestBody PaymentRequest request);
106
-
107
- // @GetMapping("/payments/{id}")
108
- // PaymentResponse getPayment(@PathVariable String id);
109
- }
110
- ```
111
-
112
- ### 3. Configuration (Infrastructure Layer)
113
-
114
- **PaymentGatewayConfig.java:**
115
- ```java
116
- package com.example.project.payment.infrastructure.external.config;
117
-
118
- import feign.Logger;
119
- import feign.RequestInterceptor;
120
- import org.springframework.beans.factory.annotation.Value;
121
- import org.springframework.context.annotation.Bean;
122
- import org.springframework.context.annotation.Configuration;
123
-
124
- /**
125
- * Configuration for PaymentGateway Feign client
126
- */
127
- @Configuration
128
- public class PaymentGatewayConfig {
129
-
130
- @Value("${external.payment-gateway.api-key:}")
131
- private String apiKey;
132
-
133
- @Bean
134
- public Logger.Level feignLoggerLevel() {
135
- return Logger.Level.FULL;
136
- }
137
-
138
- @Bean
139
- public RequestInterceptor requestInterceptor() {
140
- return requestTemplate -> {
141
- // Add headers
142
- requestTemplate.header("Content-Type", "application/json");
143
- if (apiKey != null && !apiKey.isEmpty()) {
144
- requestTemplate.header("X-API-Key", apiKey);
145
- }
146
- };
147
- }
148
- }
149
- ```
150
-
151
- ### 4. Adapter (Application Layer)
152
-
153
- **PaymentGatewayAdapter.java:**
154
- ```java
155
- package com.example.project.payment.application.services;
156
-
157
- import com.example.project.payment.domain.ports.PaymentGatewayPort;
158
- import com.example.project.payment.infrastructure.external.clients.PaymentGatewayClient;
159
- import lombok.RequiredArgsConstructor;
160
- import org.springframework.stereotype.Service;
161
-
162
- /**
163
- * Adapter implementation for PaymentGateway
164
- */
165
- @Service
166
- @RequiredArgsConstructor
167
- public class PaymentGatewayAdapter implements PaymentGatewayPort {
168
-
169
- private final PaymentGatewayClient client;
170
-
171
- // Implement port methods here
172
- // Example:
173
- // @Override
174
- // public PaymentResponse processPayment(PaymentRequest request) {
175
- // return client.processPayment(request);
176
- // }
177
- }
178
- ```
179
-
180
- ## ✨ Features
181
-
182
- ### OpenFeign Capabilities
183
- - ✅ **Declarative HTTP client** - Annotate interfaces, no implementation needed
184
- - ✅ **Request/Response mapping** - Automatic JSON serialization
185
- - ✅ **Load balancing** - Integration with Spring Cloud LoadBalancer
186
- - ✅ **Circuit breaker** - Resilience4j integration
187
- - **Logging** - Configurable request/response logging
188
- - ✅ **Error handling** - Custom error decoders
189
- - ✅ **Interceptors** - Add headers, authentication, etc.
190
-
191
- ### Hexagonal Architecture
192
- - **Port** - Domain-level interface (technology-agnostic)
193
- - ✅ **Adapter** - Application-level implementation
194
- - ✅ **Client** - Infrastructure-level Feign interface
195
- - ✅ **Configuration** - Centralized client setup
196
-
197
- ## 🔧 Configuration
198
-
199
- ### Application Properties
200
-
201
- ```yaml
202
- # application.yaml
203
-
204
- # External service configuration
205
- external:
206
- payment-gateway:
207
- url: https://api.paymentgateway.com
208
- api-key: ${PAYMENT_API_KEY}
209
- timeout:
210
- connect: 5000
211
- read: 10000
212
-
213
- # Feign configuration
214
- feign:
215
- client:
216
- config:
217
- payment-gateway:
218
- connectTimeout: 5000
219
- readTimeout: 10000
220
- loggerLevel: full
221
-
222
- # Logging
223
- logging:
224
- level:
225
- com.example.project.payment.infrastructure.external: DEBUG
226
- ```
227
-
228
- ## 🎯 Common Use Cases
229
-
230
- ### 1. Payment Gateway Integration
231
-
232
- ```java
233
- @FeignClient(name = "stripe", url = "${external.stripe.url}")
234
- public interface StripeClient {
235
-
236
- @PostMapping("/v1/charges")
237
- ChargeResponse createCharge(@RequestBody ChargeRequest request);
238
-
239
- @GetMapping("/v1/charges/{id}")
240
- ChargeResponse getCharge(@PathVariable String id);
241
-
242
- @PostMapping("/v1/refunds")
243
- RefundResponse createRefund(@RequestBody RefundRequest request);
244
- }
245
- ```
246
-
247
- ### 2. External API Integration
248
-
249
- ```java
250
- @FeignClient(name = "weather-api", url = "${external.weather.url}")
251
- public interface WeatherApiClient {
252
-
253
- @GetMapping("/current")
254
- WeatherResponse getCurrentWeather(
255
- @RequestParam String location,
256
- @RequestParam String apiKey
257
- );
258
- }
259
- ```
260
-
261
- ### 3. Microservice Communication
262
-
263
- ```java
264
- @FeignClient(name = "user-service", url = "${services.user.url}")
265
- public interface UserServiceClient {
266
-
267
- @GetMapping("/api/users/{id}")
268
- UserDto getUser(@PathVariable Long id);
269
-
270
- @PostMapping("/api/users")
271
- UserDto createUser(@RequestBody CreateUserRequest request);
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