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
|
@@ -1,10 +1,176 @@
|
|
|
1
1
|
package <%= packageName %>.<%= moduleName %>.application.usecases;
|
|
2
|
+
<%
|
|
3
|
+
// ─── Dual mode: rich (from temporal-system) or simple (from temporal-flow) ──
|
|
4
|
+
const isRichMode = typeof stubActivities !== 'undefined' && Array.isArray(stubActivities);
|
|
5
|
+
-%>
|
|
6
|
+
<% if (isRichMode) { -%>
|
|
7
|
+
<%
|
|
8
|
+
// ─── Helper: parse timeout string to Duration call ──────────────────────────
|
|
9
|
+
function durationOf(t) {
|
|
10
|
+
if (!t) return 'Duration.ofSeconds(30)';
|
|
11
|
+
return `Duration.of${t.unit}(${t.value})`;
|
|
12
|
+
}
|
|
13
|
+
-%>
|
|
14
|
+
|
|
15
|
+
import io.temporal.activity.ActivityOptions;
|
|
16
|
+
import io.temporal.common.RetryOptions;
|
|
17
|
+
<% if (isSaga) { -%>
|
|
18
|
+
import io.temporal.workflow.Saga;
|
|
19
|
+
<% } -%>
|
|
20
|
+
<% if (hasParallelSteps || hasAsyncSteps) { -%>
|
|
21
|
+
import io.temporal.workflow.Async;
|
|
22
|
+
<% } -%>
|
|
23
|
+
<% if (hasParallelSteps) { -%>
|
|
24
|
+
import io.temporal.workflow.Promise;
|
|
25
|
+
<% } -%>
|
|
26
|
+
import io.temporal.workflow.Workflow;
|
|
27
|
+
|
|
28
|
+
import java.time.Duration;
|
|
29
|
+
<% /* Collect imports for all shared + local activity interfaces and DTOs */ -%>
|
|
30
|
+
<%
|
|
31
|
+
// Build imports with a Map keyed by simple name to prevent duplicate-simple-name
|
|
32
|
+
// compile errors when a local nestedType and a shared type share the same class name.
|
|
33
|
+
// Rule: shared imports (*.shared.domain.contracts.*) always win over local ones.
|
|
34
|
+
const importMap = new Map(); // simpleName → full import statement
|
|
35
|
+
function addImport(fqn) {
|
|
36
|
+
const simpleName = fqn.split('.').pop().replace(';', '');
|
|
37
|
+
const existing = importMap.get(simpleName);
|
|
38
|
+
if (!existing) {
|
|
39
|
+
importMap.set(simpleName, fqn);
|
|
40
|
+
} else if (existing !== fqn) {
|
|
41
|
+
// Collision: prefer shared contract over local dtos/temporal
|
|
42
|
+
if (fqn.includes('.shared.domain.contracts.')) importMap.set(simpleName, fqn);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
-%>
|
|
46
|
+
<% for (const stub of stubActivities) { -%>
|
|
47
|
+
<% if (stub.isLocal) { -%>
|
|
48
|
+
<% addImport(`import ${packageName}.${moduleName}.application.ports.${stub.interfaceName};`); -%>
|
|
49
|
+
<% if (stub.hasInput) addImport(`import ${packageName}.${moduleName}.application.dtos.temporal.${stub.activityName}Input;`); -%>
|
|
50
|
+
<% if (stub.hasOutput) addImport(`import ${packageName}.${moduleName}.application.dtos.temporal.${stub.activityName}Output;`); -%>
|
|
51
|
+
<% if (stub.nestedTypeImports) for (const nti of stub.nestedTypeImports) { addImport(nti.isExternal ? `import ${packageName}.shared.domain.contracts.${nti.sourceModule}.${nti.name};` : `import ${packageName}.${moduleName}.application.dtos.temporal.${nti.name};`); } -%>
|
|
52
|
+
<% } else { -%>
|
|
53
|
+
<% addImport(`import ${packageName}.shared.domain.contracts.${stub.targetModule}.${stub.interfaceName};`); -%>
|
|
54
|
+
<% if (stub.hasInput) addImport(`import ${packageName}.shared.domain.contracts.${stub.targetModule}.${stub.activityName}Input;`); -%>
|
|
55
|
+
<% if (stub.hasOutput) addImport(`import ${packageName}.shared.domain.contracts.${stub.targetModule}.${stub.activityName}Output;`); -%>
|
|
56
|
+
<% if (stub.nestedTypeImports) for (const nti of stub.nestedTypeImports) { addImport(nti.isExternal ? `import ${packageName}.shared.domain.contracts.${nti.sourceModule}.${nti.name};` : `import ${packageName}.shared.domain.contracts.${stub.targetModule}.${nti.name};`); } -%>
|
|
57
|
+
<% } -%>
|
|
58
|
+
<% } -%>
|
|
59
|
+
<%= Array.from(importMap.values()).sort().join('\n') %>
|
|
60
|
+
|
|
61
|
+
public class <%= flowPascalCase %>WorkFlowImpl implements <%= flowPascalCase %>WorkFlow {
|
|
62
|
+
|
|
63
|
+
// ─── Activity stubs ─────────────────────────────────────────────────────
|
|
64
|
+
<% for (const stub of stubActivities) { %>
|
|
65
|
+
private final <%= stub.interfaceName %> <%= stub.stubVarName %> = Workflow.newActivityStub(
|
|
66
|
+
<%= stub.interfaceName %>.class,
|
|
67
|
+
ActivityOptions.newBuilder()
|
|
68
|
+
.setTaskQueue("<%= stub.queue %>")
|
|
69
|
+
.setStartToCloseTimeout(<%= durationOf(stub.timeout) %>)
|
|
70
|
+
.setRetryOptions(
|
|
71
|
+
RetryOptions.newBuilder()
|
|
72
|
+
.setMaximumAttempts(2)
|
|
73
|
+
.setInitialInterval(Duration.ofSeconds(1))
|
|
74
|
+
.setMaximumInterval(Duration.ofSeconds(10))
|
|
75
|
+
.setBackoffCoefficient(2.0)
|
|
76
|
+
.build()
|
|
77
|
+
).build());
|
|
78
|
+
<% } -%>
|
|
79
|
+
|
|
80
|
+
private String status = "PENDING";
|
|
81
|
+
|
|
82
|
+
@Override
|
|
83
|
+
public void start(<%= flowPascalCase %>Input input) {
|
|
84
|
+
<% if (isSaga) { -%>
|
|
85
|
+
Saga saga = new Saga(new Saga.Options.Builder().setParallelCompensation(false).build());
|
|
86
|
+
try {
|
|
87
|
+
<% } -%>
|
|
88
|
+
<%
|
|
89
|
+
// ─── Render blocks ──────────────────────────────────────────────────────────
|
|
90
|
+
const indent = isSaga ? ' ' : ' ';
|
|
91
|
+
for (const block of renderBlocks) {
|
|
92
|
+
if (block.type === 'sequential') {
|
|
93
|
+
const step = block.step;
|
|
94
|
+
-%>
|
|
95
|
+
<%= indent %>// Step <%= step.index + 1 %>: <%= step.activityName %><%= step.isLocal ? ' (local)' : ` (→ ${step.targetModule})` %><%= step.isOptional ? ' [optional]' : '' %>
|
|
96
|
+
<% if (step.isOptional) { -%>
|
|
97
|
+
<%= indent %>try {
|
|
98
|
+
<% } -%>
|
|
99
|
+
<% const optIndent = step.isOptional ? indent + ' ' : indent; -%>
|
|
100
|
+
<% if (step.hasOutput) { -%>
|
|
101
|
+
<%= optIndent %><%= step.activityName %>Output <%= step.activityCamel %>Result = <%= step.activityCamel %>Activity.execute(<% if (step.hasInput) { %>
|
|
102
|
+
<%= optIndent %> new <%= step.activityName %>Input(<%= step.inputSources.map(src => src.source === 'input' ? `input.${src.fieldName}()` : `${src.varName}.${src.fieldName}()`).join(', ') %>)<% } %>);
|
|
103
|
+
<% } else { -%>
|
|
104
|
+
<%= optIndent %><%= step.activityCamel %>Activity.execute(<% if (step.hasInput) { %>
|
|
105
|
+
<%= optIndent %> new <%= step.activityName %>Input(<%= step.inputSources.map(src => src.source === 'input' ? `input.${src.fieldName}()` : `${src.varName}.${src.fieldName}()`).join(', ') %>)<% } %>);
|
|
106
|
+
<% } -%>
|
|
107
|
+
<% if (step.compensation && isSaga) { -%>
|
|
108
|
+
<%= optIndent %>saga.addCompensation(() -> <%= step.compensation.name.charAt(0).toLowerCase() + step.compensation.name.slice(1) %>Activity.execute(
|
|
109
|
+
<%= optIndent %> new <%= step.compensation.name %>Input(<%= step.compensation.inputSources.map(src => src.source === 'input' ? `input.${src.fieldName}()` : `${src.varName}.${src.fieldName}()`).join(', ') %>)));
|
|
110
|
+
<% } -%>
|
|
111
|
+
<% if (step.isOptional) { -%>
|
|
112
|
+
<%= indent %>} catch (Exception e) {
|
|
113
|
+
<%= indent %> // Optional step — continue on failure
|
|
114
|
+
<%= indent %>}
|
|
115
|
+
<% } -%>
|
|
116
|
+
|
|
117
|
+
<% } else if (block.type === 'parallel') { -%>
|
|
118
|
+
<%= indent %>// Steps <%= block.steps.map(s => s.index + 1).join('-') %>: Parallel execution
|
|
119
|
+
<% for (const step of block.steps) { -%>
|
|
120
|
+
<% if (step.hasOutput) { -%>
|
|
121
|
+
<%= indent %>Promise<<%= step.activityName %>Output> <%= step.activityCamel %>Promise = Async.function(
|
|
122
|
+
<%= indent %> () -> <%= step.activityCamel %>Activity.execute(<% if (step.hasInput) { %>new <%= step.activityName %>Input(<%= step.inputSources.map(src => src.source === 'input' ? `input.${src.fieldName}()` : `${src.varName}.${src.fieldName}()`).join(', ') %>)<% } %>));
|
|
123
|
+
<% } else { -%>
|
|
124
|
+
<%= indent %>Promise<Void> <%= step.activityCamel %>Promise = Async.procedure(
|
|
125
|
+
<%= indent %> () -> <%= step.activityCamel %>Activity.execute(<% if (step.hasInput) { %>new <%= step.activityName %>Input(<%= step.inputSources.map(src => src.source === 'input' ? `input.${src.fieldName}()` : `${src.varName}.${src.fieldName}()`).join(', ') %>)<% } %>));
|
|
126
|
+
<% } -%>
|
|
127
|
+
<% if (step.compensation && isSaga) { -%>
|
|
128
|
+
<%= indent %>saga.addCompensation(() -> <%= step.compensation.name.charAt(0).toLowerCase() + step.compensation.name.slice(1) %>Activity.execute(
|
|
129
|
+
<%= indent %> new <%= step.compensation.name %>Input(<%= step.compensation.inputSources.map(src => src.source === 'input' ? `input.${src.fieldName}()` : `${src.varName}.${src.fieldName}()`).join(', ') %>)));
|
|
130
|
+
<% } -%>
|
|
131
|
+
<% } -%>
|
|
132
|
+
|
|
133
|
+
<%= indent %>// Wait for parallel results
|
|
134
|
+
<% for (const step of block.steps) { -%>
|
|
135
|
+
<% if (step.hasOutput) { -%>
|
|
136
|
+
<%= indent %><%= step.activityName %>Output <%= step.activityCamel %>Result = <%= step.activityCamel %>Promise.get();
|
|
137
|
+
<% } else { -%>
|
|
138
|
+
<%= indent %><%= step.activityCamel %>Promise.get();
|
|
139
|
+
<% } -%>
|
|
140
|
+
<% } -%>
|
|
141
|
+
|
|
142
|
+
<% } else if (block.type === 'async') {
|
|
143
|
+
const step = block.step;
|
|
144
|
+
-%>
|
|
145
|
+
<%= indent %>// Step <%= step.index + 1 %>: <%= step.activityName %> (fire-and-forget → <%= step.targetModule %>)
|
|
146
|
+
<%= indent %>Async.procedure(() -> <%= step.activityCamel %>Activity.execute(<% if (step.hasInput) { %>
|
|
147
|
+
<%= indent %> new <%= step.activityName %>Input(<%= step.inputSources.map(src => src.source === 'input' ? `input.${src.fieldName}()` : `${src.varName}.${src.fieldName}()`).join(', ') %>)<% } %>));
|
|
148
|
+
|
|
149
|
+
<% }
|
|
150
|
+
} /* end renderBlocks loop */ -%>
|
|
151
|
+
<% if (isSaga) { -%>
|
|
152
|
+
this.status = "COMPLETED";
|
|
153
|
+
} catch (Exception e) {
|
|
154
|
+
saga.compensate();
|
|
155
|
+
this.status = "COMPENSATED";
|
|
156
|
+
throw e;
|
|
157
|
+
}
|
|
158
|
+
<% } else { -%>
|
|
159
|
+
this.status = "COMPLETED";
|
|
160
|
+
<% } -%>
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@Override
|
|
164
|
+
public String getStatus() {
|
|
165
|
+
return this.status;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
<% } else { -%>
|
|
169
|
+
<%/* ─── Simple mode (from eva g temporal-flow) ─────────────────────────────── */%>
|
|
2
170
|
|
|
3
171
|
import io.temporal.activity.ActivityOptions;
|
|
4
172
|
import io.temporal.common.RetryOptions;
|
|
5
173
|
import io.temporal.workflow.Saga;
|
|
6
|
-
import io.temporal.workflow.SignalMethod;
|
|
7
|
-
import io.temporal.workflow.QueryMethod;
|
|
8
174
|
import io.temporal.workflow.Workflow;
|
|
9
175
|
|
|
10
176
|
import java.time.Duration;
|
|
@@ -19,7 +185,7 @@ public class <%= flowPascalCase %>WorkFlowImpl implements <%= flowPascalCase %>W
|
|
|
19
185
|
|
|
20
186
|
private final ActivityOptions lightActivityOptions = ActivityOptions.newBuilder()
|
|
21
187
|
.setStartToCloseTimeout(Duration.ofSeconds(30))
|
|
22
|
-
.setTaskQueue("
|
|
188
|
+
.setTaskQueue("<%= moduleScreamingSnake %>_LIGHT_TASK_QUEUE")
|
|
23
189
|
.setRetryOptions(
|
|
24
190
|
RetryOptions.newBuilder()
|
|
25
191
|
.setMaximumAttempts(2)
|
|
@@ -31,7 +197,7 @@ public class <%= flowPascalCase %>WorkFlowImpl implements <%= flowPascalCase %>W
|
|
|
31
197
|
|
|
32
198
|
private final ActivityOptions heavyActivityOptions = ActivityOptions.newBuilder()
|
|
33
199
|
.setStartToCloseTimeout(Duration.ofSeconds(120))
|
|
34
|
-
.setTaskQueue("
|
|
200
|
+
.setTaskQueue("<%= moduleScreamingSnake %>_HEAVY_TASK_QUEUE")
|
|
35
201
|
.setRetryOptions(
|
|
36
202
|
RetryOptions.newBuilder()
|
|
37
203
|
.setMaximumAttempts(2)
|
|
@@ -42,7 +208,7 @@ public class <%= flowPascalCase %>WorkFlowImpl implements <%= flowPascalCase %>W
|
|
|
42
208
|
).build();
|
|
43
209
|
|
|
44
210
|
@Override
|
|
45
|
-
public void start(String
|
|
211
|
+
public void start(String flowId) {
|
|
46
212
|
try {
|
|
47
213
|
//todo: workflow logic
|
|
48
214
|
} catch (Exception e) {
|
|
@@ -50,15 +216,9 @@ public class <%= flowPascalCase %>WorkFlowImpl implements <%= flowPascalCase %>W
|
|
|
50
216
|
}
|
|
51
217
|
}
|
|
52
218
|
|
|
53
|
-
@SignalMethod
|
|
54
|
-
@Override
|
|
55
|
-
public void confirm() {
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
@QueryMethod
|
|
60
219
|
@Override
|
|
61
220
|
public String getStatus() {
|
|
62
221
|
return "";
|
|
63
222
|
}
|
|
64
223
|
}
|
|
224
|
+
<% } -%>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
package <%= packageName %>.<%= moduleName %>.application.usecases;
|
|
2
|
+
<% if (inputImports && inputImports.length > 0) { %>
|
|
3
|
+
<% inputImports.forEach(imp => { -%>
|
|
4
|
+
<%= imp %>
|
|
5
|
+
<% }); -%>
|
|
6
|
+
<% } %>
|
|
7
|
+
public record <%= flowPascalCase %>Input(
|
|
8
|
+
<% inputFields.forEach((field, i) => { -%>
|
|
9
|
+
<%- field.javaType %> <%= field.name %><%= i < inputFields.length - 1 ? ',' : '' %>
|
|
10
|
+
<% }); -%>
|
|
11
|
+
) {}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
package <%= packageName %>.<%= moduleName %>.application.usecases;
|
|
2
2
|
|
|
3
3
|
import io.temporal.workflow.QueryMethod;
|
|
4
|
-
import io.temporal.workflow.SignalMethod;
|
|
5
4
|
import io.temporal.workflow.WorkflowInterface;
|
|
6
5
|
import io.temporal.workflow.WorkflowMethod;
|
|
7
6
|
|
|
8
7
|
@WorkflowInterface
|
|
9
8
|
public interface <%= flowPascalCase %>WorkFlow {
|
|
9
|
+
<% const hasTypedInput = typeof inputFields !== 'undefined' && inputFields.length > 0; -%>
|
|
10
10
|
|
|
11
11
|
@WorkflowMethod
|
|
12
|
+
<% if (hasTypedInput) { -%>
|
|
13
|
+
void start(<%= flowPascalCase %>Input input);
|
|
14
|
+
<% } else { -%>
|
|
12
15
|
void start(String flowId);
|
|
13
|
-
|
|
14
|
-
@SignalMethod
|
|
15
|
-
void confirm();
|
|
16
|
+
<% } -%>
|
|
16
17
|
|
|
17
18
|
@QueryMethod
|
|
18
19
|
String getStatus();
|
|
@@ -10,38 +10,68 @@ public class <%= flowPascalCase %>WorkFlowService {
|
|
|
10
10
|
|
|
11
11
|
private final WorkflowClient workflowClient;
|
|
12
12
|
private final String flowQueue;
|
|
13
|
+
<% const hasTypedInput = typeof inputFields !== 'undefined' && inputFields.length > 0; -%>
|
|
13
14
|
|
|
14
15
|
public <%= flowPascalCase %>WorkFlowService(
|
|
15
16
|
WorkflowClient workflowClient,
|
|
16
|
-
@Value("${temporal.flow-queue}") String flowQueue
|
|
17
|
+
@Value("${temporal.modules.<%= moduleName %>.flow-queue}") String flowQueue
|
|
17
18
|
) {
|
|
18
19
|
this.workflowClient = workflowClient;
|
|
19
20
|
this.flowQueue = flowQueue;
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
<% if (hasTypedInput) { -%>
|
|
24
|
+
/**
|
|
25
|
+
* Starts the workflow asynchronously with the full typed input.
|
|
26
|
+
*/
|
|
27
|
+
public void startAsync(String workflowId, <%= flowPascalCase %>Input input) {
|
|
28
|
+
<%= flowPascalCase %>WorkFlow workflow = getWorkflow(workflowId);
|
|
29
|
+
WorkflowClient.start(workflow::start, input);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Starts the workflow synchronously with the full typed input.
|
|
34
|
+
*/
|
|
35
|
+
public void startSync(String workflowId, <%= flowPascalCase %>Input input) {
|
|
36
|
+
<%= flowPascalCase %>WorkFlow workflow = getWorkflow(workflowId);
|
|
37
|
+
workflow.start(input);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Bridge method — starts the workflow using only the aggregate ID.
|
|
42
|
+
* <p>
|
|
43
|
+
* The generated {@code DomainEventHandler} calls
|
|
44
|
+
* {@link #startAsync(String, <%= flowPascalCase %>Input)} directly with a
|
|
45
|
+
* typed input constructed from the domain event fields. Use this overload
|
|
46
|
+
* only from custom code that can build the input by other means.
|
|
47
|
+
*
|
|
48
|
+
* @throws UnsupportedOperationException always — override or call the typed variant
|
|
49
|
+
*/
|
|
50
|
+
public void startAsync(String workflowId) {
|
|
51
|
+
throw new UnsupportedOperationException(
|
|
52
|
+
"Use startAsync(workflowId, " + "<%= flowPascalCase %>Input) — "
|
|
53
|
+
+ "the DomainEventHandler already calls the typed variant");
|
|
54
|
+
}
|
|
55
|
+
<% } else { -%>
|
|
22
56
|
public void startAsync(String flowId) {
|
|
23
|
-
<%= flowPascalCase %>WorkFlow workflow =
|
|
57
|
+
<%= flowPascalCase %>WorkFlow workflow = getWorkflow(flowId);
|
|
24
58
|
WorkflowClient.start(workflow::start, flowId);
|
|
25
59
|
}
|
|
26
60
|
|
|
27
61
|
public void startSync(String flowId) {
|
|
28
|
-
<%= flowPascalCase %>WorkFlow workflow =
|
|
62
|
+
<%= flowPascalCase %>WorkFlow workflow = getWorkflow(flowId);
|
|
29
63
|
workflow.start(flowId);
|
|
30
64
|
}
|
|
65
|
+
<% } -%>
|
|
31
66
|
|
|
32
|
-
public
|
|
33
|
-
<%= flowPascalCase %>WorkFlow workflow =
|
|
34
|
-
workflow.confirm();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public String getStatus(String flowId) {
|
|
38
|
-
<%= flowPascalCase %>WorkFlow workflow = get<%= flowPascalCase %>WorkFlow(flowId);
|
|
67
|
+
public String getStatus(String workflowId) {
|
|
68
|
+
<%= flowPascalCase %>WorkFlow workflow = getWorkflow(workflowId);
|
|
39
69
|
return workflow.getStatus();
|
|
40
70
|
}
|
|
41
71
|
|
|
42
|
-
private <%= flowPascalCase %>WorkFlow
|
|
72
|
+
private <%= flowPascalCase %>WorkFlow getWorkflow(String workflowId) {
|
|
43
73
|
WorkflowOptions options = WorkflowOptions.newBuilder()
|
|
44
|
-
.setWorkflowId(
|
|
74
|
+
.setWorkflowId(workflowId)
|
|
45
75
|
.setTaskQueue(flowQueue)
|
|
46
76
|
.build();
|
|
47
77
|
return workflowClient.newWorkflowStub(<%= flowPascalCase %>WorkFlow.class, options);
|