eva4j 1.0.11 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/AGENTS.md +441 -14
  2. package/DOMAIN_YAML_GUIDE.md +425 -21
  3. package/FUTURE_FEATURES.md +315 -115
  4. package/QUICK_REFERENCE.md +101 -153
  5. package/README.md +77 -70
  6. package/bin/eva4j.js +57 -1
  7. package/config/defaults.json +3 -0
  8. package/docs/commands/GENERATE_ENTITIES.md +662 -1968
  9. package/docs/commands/GENERATE_HTTP_EXCHANGE.md +274 -450
  10. package/docs/commands/GENERATE_KAFKA_EVENT.md +219 -498
  11. package/docs/commands/GENERATE_KAFKA_LISTENER.md +18 -18
  12. package/docs/commands/GENERATE_RECORD.md +335 -311
  13. package/docs/commands/GENERATE_TEMPORAL_ACTIVITY.md +174 -0
  14. package/docs/commands/GENERATE_TEMPORAL_FLOW.md +237 -0
  15. package/docs/commands/GENERATE_USECASE.md +216 -282
  16. package/docs/commands/INDEX.md +36 -7
  17. package/examples/doctor-evaluation.yaml +3 -3
  18. package/examples/domain-audit-complete.yaml +2 -2
  19. package/examples/domain-collections.yaml +2 -2
  20. package/examples/domain-ecommerce.yaml +2 -2
  21. package/examples/domain-events.yaml +201 -0
  22. package/examples/domain-field-visibility.yaml +11 -5
  23. package/examples/domain-multi-aggregate.yaml +12 -6
  24. package/examples/domain-one-to-many.yaml +1 -1
  25. package/examples/domain-one-to-one.yaml +1 -1
  26. package/examples/domain-secondary-onetomany.yaml +1 -1
  27. package/examples/domain-secondary-onetoone.yaml +1 -1
  28. package/examples/domain-simple.yaml +1 -1
  29. package/examples/domain-soft-delete.yaml +3 -3
  30. package/examples/domain-transitions.yaml +1 -1
  31. package/examples/domain-value-objects.yaml +1 -1
  32. package/package.json +2 -2
  33. package/src/commands/add-kafka-client.js +3 -1
  34. package/src/commands/add-temporal-client.js +286 -0
  35. package/src/commands/generate-entities.js +75 -4
  36. package/src/commands/generate-kafka-event.js +273 -89
  37. package/src/commands/generate-temporal-activity.js +228 -0
  38. package/src/commands/generate-temporal-flow.js +216 -0
  39. package/src/generators/module-generator.js +1 -0
  40. package/src/generators/shared-generator.js +26 -0
  41. package/src/utils/yaml-to-entity.js +93 -4
  42. package/templates/aggregate/AggregateRepository.java.ejs +3 -2
  43. package/templates/aggregate/AggregateRepositoryImpl.java.ejs +15 -7
  44. package/templates/aggregate/AggregateRoot.java.ejs +38 -2
  45. package/templates/aggregate/DomainEntity.java.ejs +6 -2
  46. package/templates/aggregate/DomainEventHandler.java.ejs +62 -0
  47. package/templates/aggregate/DomainEventRecord.java.ejs +50 -0
  48. package/templates/aggregate/JpaAggregateRoot.java.ejs +3 -1
  49. package/templates/aggregate/JpaEntity.java.ejs +3 -1
  50. package/templates/base/docker/kafka-services.yaml.ejs +2 -2
  51. package/templates/base/docker/temporal-services.yaml.ejs +29 -0
  52. package/templates/base/resources/parameters/develop/temporal.yaml.ejs +9 -0
  53. package/templates/base/resources/parameters/local/temporal.yaml.ejs +9 -0
  54. package/templates/base/resources/parameters/production/temporal.yaml.ejs +9 -0
  55. package/templates/base/resources/parameters/test/temporal.yaml.ejs +9 -0
  56. package/templates/base/root/AGENTS.md.ejs +916 -51
  57. package/templates/crud/Controller.java.ejs +36 -6
  58. package/templates/crud/ListQuery.java.ejs +6 -2
  59. package/templates/crud/ListQueryHandler.java.ejs +24 -10
  60. package/templates/crud/UpdateCommand.java.ejs +52 -0
  61. package/templates/crud/UpdateCommandHandler.java.ejs +105 -0
  62. package/templates/kafka-event/DomainEventHandlerMethod.ejs +1 -0
  63. package/templates/kafka-event/Event.java.ejs +23 -0
  64. package/templates/shared/application/dtos/PagedResponse.java.ejs +30 -0
  65. package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +104 -0
  66. package/templates/shared/domain/DomainEvent.java.ejs +40 -0
  67. package/templates/shared/interfaces/HeavyActivity.java.ejs +4 -0
  68. package/templates/shared/interfaces/LightActivity.java.ejs +4 -0
  69. package/templates/temporal-activity/ActivityImpl.java.ejs +14 -0
  70. package/templates/temporal-activity/ActivityInterface.java.ejs +11 -0
  71. package/templates/temporal-flow/WorkFlowImpl.java.ejs +64 -0
  72. package/templates/temporal-flow/WorkFlowInterface.java.ejs +19 -0
  73. package/templates/temporal-flow/WorkFlowService.java.ejs +49 -0
@@ -0,0 +1,174 @@
1
+ # Command `generate temporal-activity` (alias: `g temporal-activity`)
2
+
3
+ ## 📋 Description
4
+
5
+ Generates a Temporal activity with its interface (port) and Spring implementation, then registers the activity stub inside a chosen `*WorkFlowImpl.java`.
6
+
7
+ ## 🎯 Purpose
8
+
9
+ Create a single, focused unit of work that can be executed and retried independently by the Temporal server. Activities are the building blocks invoked by workflows — each activity handles one side-effect (database write, HTTP call, email, etc.) and can be categorised as **Light** (fast, < 30 s) or **Heavy** (long-running, up to 2 min) to route it to the appropriate worker queue.
10
+
11
+ ## 📝 Syntax
12
+
13
+ ```bash
14
+ eva generate temporal-activity <module>
15
+ eva g temporal-activity <module> # Short alias
16
+ ```
17
+
18
+ ### Parameters
19
+
20
+ | Parameter | Required | Description |
21
+ |-----------|----------|-------------|
22
+ | `module` | Yes | Target module where the activity **interface** lives |
23
+
24
+ > **Interactive prompts (4 steps):**
25
+ > 1. **Activity name** — e.g., `send-confirmation-email` or `SendConfirmationEmail`
26
+ > 2. **Category** — `LightActivity` (< 30 s, `LIGHT_TASK_QUEUE`) or `HeavyActivity` (up to 2 min, `HEAVY_TASK_QUEUE`)
27
+ > 3. **Workflow to register in** — lists all `*WorkFlowImpl.java` found across all modules; select the one that will invoke this activity
28
+
29
+ ## 💡 Examples
30
+
31
+ ### Example 1: Light activity for e-mail notifications
32
+
33
+ ```bash
34
+ eva g temporal-activity notification
35
+ # Prompted:
36
+ # Activity name: send-confirmation-email
37
+ # Category: LightActivity
38
+ # Workflow: order/ProcessOrderWorkFlowImpl.java
39
+ ```
40
+
41
+ **Generates:**
42
+ - `notification/application/ports/SendConfirmationEmailActivity.java` — `@ActivityInterface`
43
+ - `notification/infrastructure/adapters/activities/SendConfirmationEmailActivityImpl.java` — `@Component` implementing `LightActivity`
44
+ - Patches `order/application/usecases/ProcessOrderWorkFlowImpl.java` with a stub field
45
+
46
+ ### Example 2: Heavy activity for file processing
47
+
48
+ ```bash
49
+ eva g temporal-activity documents
50
+ # Prompted:
51
+ # Activity name: generate-pdf-report
52
+ # Category: HeavyActivity
53
+ # Workflow: documents/ProcessDocumentWorkFlowImpl.java
54
+ ```
55
+
56
+ **Generates:**
57
+ - `documents/application/ports/GeneratePdfReportActivity.java`
58
+ - `documents/infrastructure/adapters/activities/GeneratePdfReportActivityImpl.java` — implements `HeavyActivity`
59
+ - Patches `ProcessDocumentWorkFlowImpl.java` with a `heavyActivityOptions` stub
60
+
61
+ ### Example 3: Multiple activities on the same workflow
62
+
63
+ Run the command multiple times with different names to build up a workflow's activity set:
64
+
65
+ ```bash
66
+ eva g temporal-activity order # ValidateStockActivity → LightActivity
67
+ eva g temporal-activity order # ChargePaymentActivity → HeavyActivity
68
+ eva g temporal-activity order # SendReceiptActivity → LightActivity
69
+ ```
70
+
71
+ Each run adds a new stub field in the selected `WorkFlowImpl` without touching existing ones.
72
+
73
+ ## 📦 Generated Code Structure
74
+
75
+ ### Activity Interface — `SendConfirmationEmailActivity.java`
76
+
77
+ ```java
78
+ package com.example.project.notification.application.ports;
79
+
80
+ import io.temporal.activity.ActivityInterface;
81
+ import io.temporal.activity.ActivityMethod;
82
+
83
+ @ActivityInterface
84
+ public interface SendConfirmationEmailActivity {
85
+
86
+ /**
87
+ * @ActivityMethod(name = "SendConfirmationEmail") ensures a globally unique
88
+ * method name, preventing TypeAlreadyRegisteredException when multiple
89
+ * activity types are registered on the same worker.
90
+ */
91
+ @ActivityMethod(name = "SendConfirmationEmail")
92
+ void execute(String flowId);
93
+ }
94
+ ```
95
+
96
+ > **Why `name = "..."`?**
97
+ > Without an explicit name, Temporal defaults every activity method to `"Execute"`. If the same worker hosts two activity interfaces both with `execute()`, Temporal throws `TypeAlreadyRegisteredException`. The generated unique name prevents this.
98
+
99
+ ### Activity Implementation — `SendConfirmationEmailActivityImpl.java`
100
+
101
+ ```java
102
+ package com.example.project.notification.infrastructure.adapters.activities;
103
+
104
+ import com.example.project.notification.application.ports.SendConfirmationEmailActivity;
105
+ import com.example.project.shared.domain.interfaces.LightActivity;
106
+ import org.springframework.stereotype.Component;
107
+
108
+ /**
109
+ * Implements LightActivity — Spring DI injects this into TemporalConfig
110
+ * via List<LightActivity>, automatically registering it on LIGHT_TASK_QUEUE.
111
+ * No manual TemporalConfig.java patching is required.
112
+ */
113
+ @Component
114
+ public class SendConfirmationEmailActivityImpl
115
+ implements SendConfirmationEmailActivity, LightActivity {
116
+
117
+ @Override
118
+ public void execute(String flowId) {
119
+ // TODO: implement activity logic
120
+ }
121
+ }
122
+ ```
123
+
124
+ ### WorkFlowImpl — Auto-patched Stub Field
125
+
126
+ ```java
127
+ // Added inside ProcessOrderWorkFlowImpl by the command:
128
+ private final SendConfirmationEmailActivity sendConfirmationEmailActivity =
129
+ Workflow.newActivityStub(SendConfirmationEmailActivity.class, lightActivityOptions);
130
+ ```
131
+
132
+ For a `HeavyActivity`, `heavyActivityOptions` is used instead.
133
+
134
+ ## 🏗️ Queue & Registration Architecture
135
+
136
+ ```
137
+ Spring Boot startup
138
+
139
+
140
+ TemporalConfig.workerFactory()
141
+
142
+ ├─ workflowWorker (FLOW_QUEUE)
143
+ │ └─ registerWorkflowImplementationTypes(ProcessOrderWorkFlowImpl.class)
144
+
145
+ ├─ lightWorker (LIGHT_TASK_QUEUE)
146
+ │ └─ registerActivitiesImplementations(List<LightActivity> beans...)
147
+ │ ↑
148
+ │ SendConfirmationEmailActivityImpl @Component
149
+
150
+ └─ heavyWorker (HEAVY_TASK_QUEUE)
151
+ └─ registerActivitiesImplementations(List<HeavyActivity> beans...)
152
+
153
+ GeneratePdfReportActivityImpl @Component
154
+ ```
155
+
156
+ **Key points:**
157
+ - Activities are registered automatically via **Spring DI list injection** — adding `@Component` + marker interface is sufficient.
158
+ - The workflow stub field routes execution to the correct queue through `lightActivityOptions` / `heavyActivityOptions`.
159
+ - `TemporalConfig.java` is **not** patched for activities (only for workflows).
160
+
161
+ ## 📊 Light vs Heavy — Decision Guide
162
+
163
+ | Criterion | LightActivity | HeavyActivity |
164
+ |-----------|--------------|---------------|
165
+ | Max execution time | 30 seconds | 2 minutes |
166
+ | Typical use cases | Cache lookups, email dispatch, simple DB writes | PDF generation, external API calls, image processing |
167
+ | Retry timeout | 30 s per attempt | 120 s per attempt |
168
+ | Worker queue | `LIGHT_TASK_QUEUE` | `HEAVY_TASK_QUEUE` |
169
+ | Options variable in WorkFlowImpl | `lightActivityOptions` | `heavyActivityOptions` |
170
+
171
+ ## ✅ Prerequisites
172
+
173
+ - `eva add temporal-client` must have been run
174
+ - At least one workflow must exist in the project (created via `eva g temporal-flow`) — the command lists them in the interactive prompt
@@ -0,0 +1,237 @@
1
+ # Command `generate temporal-flow` (alias: `g temporal-flow`)
2
+
3
+ ## 📋 Description
4
+
5
+ Generates a complete Temporal workflow with interface, implementation and service facade, then automatically registers the workflow in the existing `TemporalConfig.java`.
6
+
7
+ ## 🎯 Purpose
8
+
9
+ Create the scaffolding for a new Temporal workflow (orchestration unit) within a module. Follows the Saga pattern with compensation support and provides both async and sync invocation modes via the generated service facade.
10
+
11
+ ## 📝 Syntax
12
+
13
+ ```bash
14
+ eva generate temporal-flow <module>
15
+ eva g temporal-flow <module> # Short alias
16
+ ```
17
+
18
+ ### Parameters
19
+
20
+ | Parameter | Required | Description |
21
+ |-----------|----------|-------------|
22
+ | `module` | Yes | Target module name (e.g., `order`, `payment`) |
23
+
24
+ > **Interactive prompt:** When the module is provided, the CLI asks for the **workflow name**. You can write it in `kebab-case` (`process-order`) or `PascalCase` (`ProcessOrder`) — both are accepted and normalised to PascalCase internally.
25
+
26
+ ## 💡 Examples
27
+
28
+ ### Example 1: Order processing workflow
29
+
30
+ ```bash
31
+ eva g temporal-flow order
32
+ # Prompted for workflow name: process-order (or ProcessOrder)
33
+ ```
34
+
35
+ **Generates:**
36
+ - `application/usecases/ProcessOrderWorkFlow.java` — `@WorkflowInterface`
37
+ - `application/usecases/ProcessOrderWorkFlowImpl.java` — implementation with Saga
38
+ - `application/usecases/ProcessOrderWorkFlowService.java` — Spring service facade
39
+ - Patches `shared/infrastructure/configurations/TemporalConfig.java` to register `ProcessOrderWorkFlowImpl`
40
+
41
+ ### Example 2: Payment workflow
42
+
43
+ ```bash
44
+ eva g temporal-flow payment
45
+ # Prompted for workflow name: process-payment
46
+ ```
47
+
48
+ **Generates:**
49
+ - `application/usecases/ProcessPaymentWorkFlow.java`
50
+ - `application/usecases/ProcessPaymentWorkFlowImpl.java`
51
+ - `application/usecases/ProcessPaymentWorkFlowService.java`
52
+ - Patches `TemporalConfig.java`
53
+
54
+ ### Example 3: Multiple workflows in the same module
55
+
56
+ You can run the command again with a different name to add more workflows:
57
+
58
+ ```bash
59
+ eva g temporal-flow order
60
+ # process-order → generates ProcessOrder files
61
+
62
+ eva g temporal-flow order
63
+ # refund-order → generates RefundOrder files
64
+ ```
65
+
66
+ Each run appends a new `registerWorkflowImplementationTypes(...)` entry to `TemporalConfig.java` without duplicating existing registrations.
67
+
68
+ ## 📦 Generated Code Structure
69
+
70
+ ### WorkFlow Interface — `ProcessOrderWorkFlow.java`
71
+
72
+ ```java
73
+ package com.example.project.order.application.usecases;
74
+
75
+ import io.temporal.workflow.QueryMethod;
76
+ import io.temporal.workflow.SignalMethod;
77
+ import io.temporal.workflow.WorkflowInterface;
78
+ import io.temporal.workflow.WorkflowMethod;
79
+
80
+ @WorkflowInterface
81
+ public interface ProcessOrderWorkFlow {
82
+
83
+ @WorkflowMethod
84
+ void start(String input);
85
+
86
+ @SignalMethod
87
+ void confirm();
88
+
89
+ @QueryMethod
90
+ String getStatus();
91
+ }
92
+ ```
93
+
94
+ ### WorkFlow Implementation — `ProcessOrderWorkFlowImpl.java`
95
+
96
+ ```java
97
+ package com.example.project.order.application.usecases;
98
+
99
+ import io.temporal.activity.ActivityOptions;
100
+ import io.temporal.common.RetryOptions;
101
+ import io.temporal.workflow.Saga;
102
+ import io.temporal.workflow.SignalMethod;
103
+ import io.temporal.workflow.QueryMethod;
104
+ import io.temporal.workflow.Workflow;
105
+
106
+ import java.time.Duration;
107
+
108
+ public class ProcessOrderWorkFlowImpl implements ProcessOrderWorkFlow {
109
+
110
+ private Saga.Options sagaOptions = new Saga.Options.Builder()
111
+ .setParallelCompensation(false)
112
+ .build();
113
+
114
+ private Saga saga = new Saga(sagaOptions);
115
+
116
+ // Light activities (<30 s) — routed to LIGHT_TASK_QUEUE
117
+ private final ActivityOptions lightActivityOptions = ActivityOptions.newBuilder()
118
+ .setStartToCloseTimeout(Duration.ofSeconds(30))
119
+ .setTaskQueue("LIGHT_TASK_QUEUE")
120
+ .setRetryOptions(
121
+ RetryOptions.newBuilder()
122
+ .setMaximumAttempts(2)
123
+ .setInitialInterval(Duration.ofSeconds(1))
124
+ .setMaximumInterval(Duration.ofSeconds(10))
125
+ .setBackoffCoefficient(2.0)
126
+ .build()
127
+ ).build();
128
+
129
+ // Heavy activities (up to 2 min) — routed to HEAVY_TASK_QUEUE
130
+ private final ActivityOptions heavyActivityOptions = ActivityOptions.newBuilder()
131
+ .setStartToCloseTimeout(Duration.ofSeconds(120))
132
+ .setTaskQueue("HEAVY_TASK_QUEUE")
133
+ .setRetryOptions(
134
+ RetryOptions.newBuilder()
135
+ .setMaximumAttempts(2)
136
+ .setInitialInterval(Duration.ofSeconds(1))
137
+ .setMaximumInterval(Duration.ofSeconds(10))
138
+ .setBackoffCoefficient(2.0)
139
+ .build()
140
+ ).build();
141
+
142
+ private String status = "PENDING";
143
+
144
+ @Override
145
+ public void start(String workFlowId) {
146
+ try {
147
+ //todo: workflow logic
148
+ } catch (Exception e) {
149
+ saga.compensate();
150
+ }
151
+ }
152
+
153
+ @Override
154
+ public void confirm() {
155
+ // Handle confirmation signal
156
+ }
157
+
158
+ @Override
159
+ public String getStatus() {
160
+ return status;
161
+ }
162
+ }
163
+ ```
164
+
165
+ ### WorkFlow Service Facade — `ProcessOrderWorkFlowService.java`
166
+
167
+ ```java
168
+ package com.example.project.order.application.usecases;
169
+
170
+ import com.example.project.shared.domain.annotations.ApplicationComponent;
171
+ import io.temporal.client.WorkflowClient;
172
+ import io.temporal.client.WorkflowOptions;
173
+ import org.springframework.beans.factory.annotation.Value;
174
+
175
+ import java.util.UUID;
176
+
177
+ @ApplicationComponent
178
+ public class ProcessOrderWorkFlowService {
179
+
180
+ private final WorkflowClient workflowClient;
181
+
182
+ @Value("${temporal.flow-queue}")
183
+ private String flowQueue;
184
+
185
+ // ... constructor injection
186
+
187
+ /** Fire and forget — returns the workflow ID immediately */
188
+ public String startAsync(String input) {
189
+ String workflowId = "ProcessOrderWorkFlow-" + UUID.randomUUID();
190
+ ProcessOrderWorkFlow workflow = workflowClient.newWorkflowStub(
191
+ ProcessOrderWorkFlow.class,
192
+ WorkflowOptions.newBuilder()
193
+ .setWorkflowId(workflowId)
194
+ .setTaskQueue(flowQueue)
195
+ .build());
196
+ WorkflowClient.start(workflow::start, input);
197
+ return workflowId;
198
+ }
199
+
200
+ /** Blocking — waits until the workflow finishes */
201
+ public void startSync(String input) {
202
+ String workflowId = "ProcessOrderWorkFlow-" + UUID.randomUUID();
203
+ ProcessOrderWorkFlow workflow = workflowClient.newWorkflowStub(
204
+ ProcessOrderWorkFlow.class,
205
+ WorkflowOptions.newBuilder()
206
+ .setWorkflowId(workflowId)
207
+ .setTaskQueue(flowQueue)
208
+ .build());
209
+ workflow.start(input);
210
+ }
211
+
212
+ public void confirm(String workflowId) { /* signal */ }
213
+ public String getStatus(String workflowId) { /* query */ return null; }
214
+ }
215
+ ```
216
+
217
+ ### TemporalConfig.java — Auto-patched Entry
218
+
219
+ ```java
220
+ // registered automatically by eva g temporal-flow
221
+ workflowWorker.registerWorkflowImplementationTypes(ProcessOrderWorkFlowImpl.class);
222
+ ```
223
+
224
+ ## 🏗️ Queue Architecture
225
+
226
+ | Queue | Purpose |
227
+ |-------|---------|
228
+ | `FLOW_QUEUE` | Workflow orchestration (WorkFlowImpl runs here) |
229
+ | `LIGHT_TASK_QUEUE` | Fast activities (< 30 s), injected via `lightActivityOptions` |
230
+ | `HEAVY_TASK_QUEUE` | Long-running activities (up to 2 min), injected via `heavyActivityOptions` |
231
+
232
+ Activity stubs created inside the workflow must pass the matching options object — see [GENERATE_TEMPORAL_ACTIVITY.md](./GENERATE_TEMPORAL_ACTIVITY.md).
233
+
234
+ ## ✅ Prerequisites
235
+
236
+ - `eva add temporal-client` must have been run before this command
237
+ - The target `<module>` must already exist (`eva add module <module>`)