eva4j 1.0.16 → 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.
Files changed (151) hide show
  1. package/AGENTS.md +220 -5
  2. package/DOMAIN_YAML_GUIDE.md +188 -3
  3. package/FUTURE_FEATURES.md +33 -52
  4. package/QUICK_REFERENCE.md +8 -4
  5. package/bin/eva4j.js +70 -2
  6. package/config/defaults.json +1 -0
  7. package/docs/CAMUNDA_DMN_GUIDE.md +1380 -0
  8. package/docs/KAFKA_PRODUCTION_CONFIG.md +441 -0
  9. package/docs/RABBITMQ_PRODUCTION_CONFIG.md +227 -0
  10. package/docs/commands/ADD_RABBITMQ_CLIENT.md +192 -0
  11. package/docs/commands/EVALUATE_SYSTEM.md +290 -10
  12. package/docs/commands/GENERATE_RABBITMQ_EVENT.md +341 -0
  13. package/docs/commands/GENERATE_RABBITMQ_LISTENER.md +595 -0
  14. package/docs/commands/GENERATE_TEMPORAL_FLOW.md +52 -12
  15. package/docs/commands/INDEX.md +27 -3
  16. package/docs/prototype/TEMPORAL_COMMUNICATION_PATTERNS.md +731 -0
  17. package/docs/prototype/TEMPORAL_DESIGN_METHODOLOGY.md +740 -0
  18. package/docs/prototype/system/RISKS.md +277 -0
  19. package/docs/prototype/system/customers.yaml +133 -0
  20. package/docs/prototype/system/inventory.yaml +109 -0
  21. package/docs/prototype/system/notifications.yaml +131 -0
  22. package/docs/prototype/system/orders.yaml +241 -0
  23. package/docs/prototype/system/payments.yaml +256 -0
  24. package/docs/prototype/system/products.yaml +168 -0
  25. package/docs/prototype/system/system.yaml +269 -0
  26. package/examples/domain-endpoints-multi-aggregate.yaml +140 -0
  27. package/examples/domain-events.yaml +26 -0
  28. package/examples/domain-read-models.yaml +113 -0
  29. package/examples/system/customer.yaml +89 -0
  30. package/examples/system/orders.yaml +119 -0
  31. package/examples/system/product.yaml +27 -0
  32. package/examples/system/system.yaml +80 -0
  33. package/package.json +1 -1
  34. package/read-model-spec.md +664 -0
  35. package/src/agents/design-gap-analyst-temporal.agent.md +452 -0
  36. package/src/agents/design-gap-analyst.agent.md +383 -0
  37. package/src/agents/design-reviewer-temporal.agent.md +412 -0
  38. package/src/agents/design-reviewer.agent.md +34 -5
  39. package/src/agents/implement-use-cases.prompt.md +179 -0
  40. package/src/agents/ux-gap-analyst.agent.md +412 -0
  41. package/src/commands/add-rabbitmq-client.js +261 -0
  42. package/src/commands/add-temporal-client.js +22 -2
  43. package/src/commands/build.js +267 -11
  44. package/src/commands/evaluate-system.js +700 -13
  45. package/src/commands/generate-entities.js +560 -24
  46. package/src/commands/generate-http-exchange.js +3 -0
  47. package/src/commands/generate-kafka-event.js +3 -0
  48. package/src/commands/generate-kafka-listener.js +3 -0
  49. package/src/commands/generate-rabbitmq-event.js +665 -0
  50. package/src/commands/generate-rabbitmq-listener.js +205 -0
  51. package/src/commands/generate-record.js +2 -2
  52. package/src/commands/generate-resource.js +4 -1
  53. package/src/commands/generate-temporal-activity.js +970 -33
  54. package/src/commands/generate-temporal-flow.js +98 -38
  55. package/src/commands/generate-temporal-system.js +708 -0
  56. package/src/commands/generate-usecase.js +4 -1
  57. package/src/skills/build-system-yaml/SKILL.md +343 -2
  58. package/src/skills/build-system-yaml/references/domain-yaml-spec.md +253 -26
  59. package/src/skills/build-system-yaml/references/module-spec.md +90 -9
  60. package/src/skills/build-system-yaml/references/system-yaml-spec.md +36 -0
  61. package/src/skills/build-temporal-system/SKILL.md +752 -0
  62. package/src/skills/build-temporal-system/references/temporal-communication-patterns.md +167 -0
  63. package/src/skills/build-temporal-system/references/temporal-domain-yaml-spec.md +449 -0
  64. package/src/skills/build-temporal-system/references/temporal-module-spec.md +353 -0
  65. package/src/skills/build-temporal-system/references/temporal-system-yaml-spec.md +326 -0
  66. package/src/skills/implement-use-case/SKILL.md +350 -0
  67. package/src/skills/implement-use-case/references/use-case-patterns.md +980 -0
  68. package/src/skills/requirements-elicitation/SKILL.md +228 -0
  69. package/src/skills/requirements-elicitation/references/interview-framework.md +260 -0
  70. package/src/skills/requirements-elicitation/references/output-templates.md +368 -0
  71. package/src/utils/bounded-context-diagram.js +844 -0
  72. package/src/utils/config-manager.js +4 -2
  73. package/src/utils/domain-validator.js +495 -17
  74. package/src/utils/naming.js +20 -0
  75. package/src/utils/system-validator.js +169 -11
  76. package/src/utils/system-yaml-parser.js +318 -0
  77. package/src/utils/temporal-validator.js +497 -0
  78. package/src/utils/validator.js +3 -1
  79. package/src/utils/yaml-to-entity.js +281 -9
  80. package/templates/aggregate/AggregateRepository.java.ejs +4 -0
  81. package/templates/aggregate/AggregateRepositoryImpl.java.ejs +8 -0
  82. package/templates/aggregate/AggregateRoot.java.ejs +38 -4
  83. package/templates/aggregate/DomainEventHandler.java.ejs +116 -22
  84. package/templates/aggregate/JpaAggregateRoot.java.ejs +4 -4
  85. package/templates/aggregate/JpaEntity.java.ejs +2 -2
  86. package/templates/base/docker/rabbitmq-services.yaml.ejs +12 -0
  87. package/templates/base/resources/parameters/develop/kafka.yaml.ejs +5 -0
  88. package/templates/base/resources/parameters/develop/rabbitmq.yaml.ejs +15 -0
  89. package/templates/base/resources/parameters/develop/temporal.yaml.ejs +0 -3
  90. package/templates/base/resources/parameters/local/kafka.yaml.ejs +5 -0
  91. package/templates/base/resources/parameters/local/rabbitmq.yaml.ejs +15 -0
  92. package/templates/base/resources/parameters/local/temporal.yaml.ejs +0 -3
  93. package/templates/base/resources/parameters/production/kafka.yaml.ejs +39 -8
  94. package/templates/base/resources/parameters/production/rabbitmq.yaml.ejs +32 -0
  95. package/templates/base/resources/parameters/production/temporal.yaml.ejs +0 -3
  96. package/templates/base/resources/parameters/test/kafka.yaml.ejs +12 -6
  97. package/templates/base/resources/parameters/test/rabbitmq.yaml.ejs +15 -0
  98. package/templates/base/resources/parameters/test/temporal.yaml.ejs +0 -3
  99. package/templates/base/root/AGENTS.md.ejs +1 -1
  100. package/templates/crud/DeleteCommandHandler.java.ejs +19 -1
  101. package/templates/crud/EndpointsController.java.ejs +1 -1
  102. package/templates/crud/ScaffoldCommand.java.ejs +5 -2
  103. package/templates/crud/ScaffoldCommandHandler.java.ejs +3 -1
  104. package/templates/crud/ScaffoldQuery.java.ejs +5 -2
  105. package/templates/crud/ScaffoldQueryHandler.java.ejs +3 -1
  106. package/templates/crud/SubEntityRemoveCommand.java.ejs +1 -1
  107. package/templates/crud/UpdateCommandHandler.java.ejs +53 -2
  108. package/templates/evaluate/report.html.ejs +1447 -90
  109. package/templates/kafka-event/KafkaConfigBean.java.ejs +1 -1
  110. package/templates/kafka-event/KafkaMessageBroker.java.ejs +3 -3
  111. package/templates/ports/PortAclMapper.java.ejs +35 -0
  112. package/templates/ports/PortFeignAdapter.java.ejs +7 -22
  113. package/templates/ports/PortFeignClient.java.ejs +4 -0
  114. package/templates/ports/PortResponseDto.java.ejs +1 -1
  115. package/templates/rabbitmq-event/RabbitConfigBean.java.ejs +33 -0
  116. package/templates/rabbitmq-event/RabbitConfigExchange.java.ejs +12 -0
  117. package/templates/rabbitmq-event/RabbitMessageBroker.java.ejs +35 -0
  118. package/templates/rabbitmq-event/RabbitMessageBrokerMethod.java.ejs +9 -0
  119. package/templates/rabbitmq-listener/RabbitConfigConsumerBean.java.ejs +33 -0
  120. package/templates/rabbitmq-listener/RabbitConfigConsumerExchange.java.ejs +12 -0
  121. package/templates/rabbitmq-listener/RabbitListenerClass.java.ejs +82 -0
  122. package/templates/rabbitmq-listener/RabbitListenerSimple.java.ejs +56 -0
  123. package/templates/read-model/ReadModelDomain.java.ejs +46 -0
  124. package/templates/read-model/ReadModelJpa.java.ejs +58 -0
  125. package/templates/read-model/ReadModelJpaRepository.java.ejs +13 -0
  126. package/templates/read-model/ReadModelKafkaListener.java.ejs +64 -0
  127. package/templates/read-model/ReadModelRabbitListener.java.ejs +71 -0
  128. package/templates/read-model/ReadModelRepository.java.ejs +42 -0
  129. package/templates/read-model/ReadModelRepositoryImpl.java.ejs +85 -0
  130. package/templates/read-model/ReadModelSyncHandler.java.ejs +54 -0
  131. package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +18 -4
  132. package/templates/shared/configurations/rabbitmqConfig/RabbitMQConfig.java.ejs +100 -0
  133. package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +2 -64
  134. package/templates/shared/configurations/temporalConfig/TemporalWorkerFactoryLifecycle.java.ejs +41 -0
  135. package/templates/temporal-activity/ActivityImpl.java.ejs +68 -2
  136. package/templates/temporal-activity/ActivityInput.java.ejs +14 -0
  137. package/templates/temporal-activity/ActivityInterface.java.ejs +7 -1
  138. package/templates/temporal-activity/ActivityOutput.java.ejs +14 -0
  139. package/templates/temporal-activity/NestedType.java.ejs +12 -0
  140. package/templates/temporal-activity/SharedActivityInput.java.ejs +14 -0
  141. package/templates/temporal-activity/SharedActivityInterface.java.ejs +15 -0
  142. package/templates/temporal-activity/SharedActivityOutput.java.ejs +14 -0
  143. package/templates/temporal-activity/SharedNestedType.java.ejs +12 -0
  144. package/templates/temporal-flow/ModuleHeavyActivity.java.ejs +6 -0
  145. package/templates/temporal-flow/ModuleLightActivity.java.ejs +6 -0
  146. package/templates/temporal-flow/ModuleTemporalWorkerConfig.java.ejs +58 -0
  147. package/templates/temporal-flow/WorkFlowImpl.java.ejs +172 -12
  148. package/templates/temporal-flow/WorkFlowInput.java.ejs +11 -0
  149. package/templates/temporal-flow/WorkFlowInterface.java.ejs +5 -4
  150. package/templates/temporal-flow/WorkFlowService.java.ejs +42 -12
  151. package/COMMAND_EVALUATION.md +0 -911
@@ -0,0 +1,205 @@
1
+ const inquirer = require('inquirer');
2
+ const ora = require('ora');
3
+ const chalk = require('chalk');
4
+ const path = require('path');
5
+ const fs = require('fs-extra');
6
+ const yaml = require('js-yaml');
7
+ const ConfigManager = require('../utils/config-manager');
8
+ const { isEva4jProject } = require('../utils/validator');
9
+ const { toPackagePath, toPascalCase, toCamelCase, toKebabCase } = require('../utils/naming');
10
+ const { renderAndWrite, renderTemplate } = require('../utils/template-engine');
11
+
12
+ /**
13
+ * Generate RabbitMQ listener methods in a module's infrastructure
14
+ * @param {string} moduleName - Name of the module
15
+ */
16
+ async function generateRabbitMQListenerCommand(moduleName) {
17
+ const projectDir = process.cwd();
18
+
19
+ // Validate eva4j project
20
+ if (!(await isEva4jProject(projectDir))) {
21
+ console.error(chalk.red('❌ Not in an eva4j project directory'));
22
+ console.error(chalk.gray(' Run this command from the root of an eva4j project'));
23
+ process.exit(1);
24
+ }
25
+
26
+ // Check if RabbitMQ is installed
27
+ const configManager = new ConfigManager(projectDir);
28
+ if (!(await configManager.featureExists('rabbitmq'))) {
29
+ console.error(chalk.red('❌ RabbitMQ client is not installed in this project'));
30
+ console.error(chalk.gray(' Run: eva4j add rabbitmq-client'));
31
+ process.exit(1);
32
+ }
33
+
34
+ // Load project configuration
35
+ const projectConfig = await configManager.loadProjectConfig();
36
+ const { packageName, projectName } = projectConfig;
37
+ const packagePath = toPackagePath(packageName);
38
+
39
+ // Normalise module name to camelCase
40
+ moduleName = toCamelCase(moduleName);
41
+
42
+ // Validate module exists
43
+ if (!(await configManager.moduleExists(moduleName))) {
44
+ console.error(chalk.red(`❌ Module '${moduleName}' not found in project`));
45
+ console.error(chalk.gray(' Available modules:'));
46
+ const modules = projectConfig.modules || [];
47
+ modules.forEach(mod => console.error(chalk.gray(` - ${mod}`)));
48
+ process.exit(1);
49
+ }
50
+
51
+ // Read available queues from rabbitmq.yaml
52
+ const queues = await getAvailableQueues(projectDir);
53
+
54
+ if (queues.length === 0) {
55
+ console.error(chalk.red('❌ No queues found in rabbitmq.yaml'));
56
+ console.error(chalk.gray(' Add queues using: eva4j generate rabbitmq-event <module> <event-name>'));
57
+ process.exit(1);
58
+ }
59
+
60
+ // Prompt for queue selection (multiple)
61
+ const { selectedQueues } = await inquirer.prompt([
62
+ {
63
+ type: 'checkbox',
64
+ name: 'selectedQueues',
65
+ message: 'Select queues to listen to (use space to select, enter to confirm):',
66
+ choices: queues.map(q => ({
67
+ name: `${q.key} (${q.value})`,
68
+ value: q.key,
69
+ checked: false
70
+ })),
71
+ validate: (answer) => {
72
+ if (answer.length === 0) {
73
+ return 'You must select at least one queue';
74
+ }
75
+ return true;
76
+ }
77
+ }
78
+ ]);
79
+
80
+ const spinner = ora('Generating RabbitMQ listeners...').start();
81
+
82
+ try {
83
+ const generatedListeners = [];
84
+
85
+ for (const queueKey of selectedQueues) {
86
+ const listenerContext = buildQueueContext(packageName, moduleName, queueKey, queues);
87
+
88
+ const listenerPath = path.join(
89
+ projectDir,
90
+ 'src',
91
+ 'main',
92
+ 'java',
93
+ packagePath,
94
+ moduleName,
95
+ 'infrastructure',
96
+ 'rabbitListener',
97
+ `${listenerContext.listenerClassName}.java`
98
+ );
99
+
100
+ // Check if listener already exists
101
+ if (await fs.pathExists(listenerPath)) {
102
+ console.log(chalk.yellow(` ⚠ ${listenerContext.listenerClassName}.java already exists, skipping...`));
103
+ continue;
104
+ }
105
+
106
+ spinner.text = `Generating ${listenerContext.listenerClassName}...`;
107
+
108
+ const templatePath = path.join(__dirname, '..', '..', 'templates', 'rabbitmq-listener', 'RabbitListenerSimple.java.ejs');
109
+ await renderAndWrite(templatePath, listenerPath, listenerContext);
110
+
111
+ generatedListeners.push(listenerContext.listenerClassName);
112
+ }
113
+
114
+ if (generatedListeners.length === 0) {
115
+ spinner.warn(chalk.yellow('No new listeners were generated (all already exist)'));
116
+ } else {
117
+ spinner.succeed(chalk.green(`✨ ${generatedListeners.length} RabbitMQ listener(s) generated successfully!`));
118
+ }
119
+
120
+ // Display generated components
121
+ console.log(chalk.blue('\n📦 Generated components:'));
122
+ generatedListeners.forEach(className => {
123
+ console.log(chalk.gray(` ├── ${moduleName}/infrastructure/rabbitListener/${className}.java`));
124
+ });
125
+
126
+ if (generatedListeners.length > 0) {
127
+ console.log(chalk.blue('\n✅ Listeners configured:'));
128
+ selectedQueues.forEach(queue => {
129
+ const className = generateListenerClassName(queue, moduleName);
130
+ console.log(chalk.gray(` ├── ${className} - listening to: ${queue}`));
131
+ });
132
+
133
+ console.log(chalk.yellow('\n💡 Next steps:'));
134
+ console.log(chalk.gray(' 1. Implement event processing logic in each listener\'s handle() method'));
135
+ console.log(chalk.gray(' 2. Consider creating use cases to handle events via UseCaseMediator'));
136
+ console.log(chalk.gray(' 3. Test your listeners with RabbitMQ producer'));
137
+ }
138
+
139
+ } catch (error) {
140
+ spinner.fail(chalk.red('Failed to generate RabbitMQ listener'));
141
+ console.error(chalk.red('\n❌ Error:'), error.message);
142
+ if (process.env.DEBUG) {
143
+ console.error(error.stack);
144
+ }
145
+ process.exit(1);
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Read available queues from rabbitmq.yaml
151
+ */
152
+ async function getAvailableQueues(projectDir) {
153
+ const rabbitYmlPath = path.join(projectDir, 'src', 'main', 'resources', 'parameters', 'local', 'rabbitmq.yaml');
154
+
155
+ if (!(await fs.pathExists(rabbitYmlPath))) {
156
+ return [];
157
+ }
158
+
159
+ const rabbitContent = await fs.readFile(rabbitYmlPath, 'utf8');
160
+ const rabbitConfig = yaml.load(rabbitContent);
161
+
162
+ if (!rabbitConfig.queues || Object.keys(rabbitConfig.queues).length === 0) {
163
+ return [];
164
+ }
165
+
166
+ return Object.entries(rabbitConfig.queues).map(([key, value]) => ({
167
+ key,
168
+ value
169
+ }));
170
+ }
171
+
172
+ /**
173
+ * Build context object for a queue
174
+ */
175
+ function buildQueueContext(packageName, moduleName, queueKey, allQueues) {
176
+ const queue = allQueues.find(q => q.key === queueKey);
177
+ const topicValue = queue.value;
178
+
179
+ const listenerClassName = generateListenerClassName(queueKey, moduleName);
180
+ const listenerBeanName = toCamelCase(listenerClassName);
181
+ const topicVariableName = toCamelCase(queueKey);
182
+
183
+ return {
184
+ packageName,
185
+ moduleName,
186
+ topicNameKebab: queueKey,
187
+ topicValue,
188
+ topicSpringProperty: `\${queues.${queueKey}}`,
189
+ topicVariableName,
190
+ listenerClassName,
191
+ listenerBeanName
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Generate listener class name from queue key and module name
197
+ * Example: order-placed + notification → NotificationOrderPlacedListener
198
+ */
199
+ function generateListenerClassName(queueKey, moduleName) {
200
+ const modulePrefix = toPascalCase(moduleName);
201
+ const topicName = toPascalCase(queueKey);
202
+ return `${modulePrefix}${topicName}Listener`;
203
+ }
204
+
205
+ module.exports = generateRabbitMQListenerCommand;
@@ -6,7 +6,7 @@ const inquirer = require('inquirer');
6
6
  const clipboardy = require('clipboardy');
7
7
  const ConfigManager = require('../utils/config-manager');
8
8
  const { isEva4jProject } = require('../utils/validator');
9
- const { toPackagePath, toPascalCase } = require('../utils/naming');
9
+ const { toPackagePath, toPascalCase, toCamelCase } = require('../utils/naming');
10
10
  const { renderAndWrite } = require('../utils/template-engine');
11
11
  const { parseJsonToRecords } = require('../utils/json-to-java');
12
12
 
@@ -107,7 +107,7 @@ async function generateRecordCommand(options = {}) {
107
107
  const { recordName, moduleName, targetFolder } = answers;
108
108
 
109
109
  // Validate module exists in filesystem
110
- const modulePath = path.join(projectDir, 'src', 'main', 'java', packagePath, moduleName);
110
+ const modulePath = path.join(projectDir, 'src', 'main', 'java', packagePath, toCamelCase(moduleName));
111
111
  if (!(await fs.pathExists(modulePath))) {
112
112
  console.error(chalk.red(`❌ Module '${moduleName}' not found in filesystem`));
113
113
  process.exit(1);
@@ -5,7 +5,7 @@ const path = require('path');
5
5
  const fs = require('fs-extra');
6
6
  const ConfigManager = require('../utils/config-manager');
7
7
  const { isEva4jProject } = require('../utils/validator');
8
- const { toPackagePath, toPascalCase, pluralizeWord, toKebabCase } = require('../utils/naming');
8
+ const { toPackagePath, toPascalCase, pluralizeWord, toKebabCase, toCamelCase } = require('../utils/naming');
9
9
  const { renderAndWrite } = require('../utils/template-engine');
10
10
  const ChecksumManager = require('../utils/checksum-manager');
11
11
 
@@ -30,6 +30,9 @@ async function generateResourceCommand(moduleName, options = {}) {
30
30
  const { packageName } = projectConfig;
31
31
  const packagePath = toPackagePath(packageName);
32
32
 
33
+ // Normalise module name to camelCase (system.yaml uses kebab-case, .eva4j.json stores camelCase)
34
+ moduleName = toCamelCase(moduleName);
35
+
33
36
  // Validate module exists
34
37
  if (!(await configManager.moduleExists(moduleName))) {
35
38
  console.error(chalk.red(`❌ Module '${moduleName}' not found in project`));