eva4j 1.0.13 → 1.0.14

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 (44) hide show
  1. package/AGENTS.md +51 -9
  2. package/DOMAIN_YAML_GUIDE.md +150 -0
  3. package/bin/eva4j.js +31 -1
  4. package/design-system.md +797 -0
  5. package/docs/commands/EVALUATE_SYSTEM.md +542 -0
  6. package/docs/commands/GENERATE_ENTITIES.md +196 -0
  7. package/docs/commands/INDEX.md +10 -1
  8. package/examples/domain-endpoints-relations.yaml +353 -0
  9. package/examples/domain-endpoints-versioned.yaml +144 -0
  10. package/examples/domain-endpoints.yaml +135 -0
  11. package/examples/system.yaml +289 -0
  12. package/package.json +1 -1
  13. package/src/commands/create.js +6 -3
  14. package/src/commands/evaluate-system.js +384 -0
  15. package/src/commands/generate-entities.js +677 -14
  16. package/src/commands/generate-kafka-event.js +59 -5
  17. package/src/commands/generate-system.js +243 -0
  18. package/src/generators/base-generator.js +9 -1
  19. package/src/utils/naming.js +3 -2
  20. package/src/utils/system-validator.js +314 -0
  21. package/src/utils/yaml-to-entity.js +31 -2
  22. package/templates/aggregate/AggregateRepository.java.ejs +5 -0
  23. package/templates/aggregate/AggregateRepositoryImpl.java.ejs +9 -0
  24. package/templates/aggregate/DomainEventHandler.java.ejs +24 -20
  25. package/templates/aggregate/JpaRepository.java.ejs +5 -0
  26. package/templates/base/root/skill-build-domain-yaml-references-generate-entities.md.ejs +1103 -0
  27. package/templates/base/root/skill-build-domain-yaml.ejs +292 -0
  28. package/templates/base/root/skill-build-system-yaml.ejs +252 -0
  29. package/templates/base/root/system.yaml.ejs +97 -0
  30. package/templates/crud/EndpointsController.java.ejs +178 -0
  31. package/templates/crud/FindByQuery.java.ejs +17 -0
  32. package/templates/crud/FindByQueryHandler.java.ejs +57 -0
  33. package/templates/crud/ScaffoldCommand.java.ejs +12 -0
  34. package/templates/crud/ScaffoldCommandHandler.java.ejs +43 -0
  35. package/templates/crud/ScaffoldQuery.java.ejs +12 -0
  36. package/templates/crud/ScaffoldQueryHandler.java.ejs +40 -0
  37. package/templates/crud/SubEntityAddCommand.java.ejs +17 -0
  38. package/templates/crud/SubEntityAddCommandHandler.java.ejs +43 -0
  39. package/templates/crud/SubEntityRemoveCommand.java.ejs +9 -0
  40. package/templates/crud/SubEntityRemoveCommandHandler.java.ejs +42 -0
  41. package/templates/crud/TransitionCommand.java.ejs +9 -0
  42. package/templates/crud/TransitionCommandHandler.java.ejs +39 -0
  43. package/templates/evaluate/report.html.ejs +971 -0
  44. package/templates/kafka-event/Event.java.ejs +7 -0
@@ -0,0 +1,178 @@
1
+ <%
2
+ // ── Classify operations for selective imports ────────────────────────────
3
+ const uniqueOpsMap = new Map();
4
+ operations.forEach(op => { if (!uniqueOpsMap.has(op.useCase)) uniqueOpsMap.set(op.useCase, op); });
5
+ const uniqueOps = Array.from(uniqueOpsMap.values());
6
+
7
+ const hasCreate = uniqueOps.some(op => op.isStandard && op.standardType === 'create');
8
+ const hasUpdate = uniqueOps.some(op => op.isStandard && op.standardType === 'update');
9
+ const hasDelete = uniqueOps.some(op => op.isStandard && op.standardType === 'delete');
10
+ const hasGetById = uniqueOps.some(op => op.isStandard && op.standardType === 'getById');
11
+ const hasFindAll = uniqueOps.some(op => op.isStandard && op.standardType === 'findAll');
12
+ const hasTransition = uniqueOps.some(op => op.classifiedType === 'transition');
13
+ const hasSubAdd = uniqueOps.some(op => op.classifiedType === 'subEntityAdd');
14
+ const hasSubRemove = uniqueOps.some(op => op.classifiedType === 'subEntityRemove');
15
+ const hasFindBy = uniqueOps.some(op => op.classifiedType === 'findBy');
16
+
17
+ const transitionOps = uniqueOps.filter(op => op.classifiedType === 'transition');
18
+ const subAddOps = uniqueOps.filter(op => op.classifiedType === 'subEntityAdd');
19
+ const subRemoveOps = uniqueOps.filter(op => op.classifiedType === 'subEntityRemove');
20
+ const findByOps = uniqueOps.filter(op => op.classifiedType === 'findBy');
21
+ const customCmdUCs = uniqueOps.filter(op => op.classifiedType === 'scaffold' && op.type !== 'query');
22
+ const customQueryUCs = uniqueOps.filter(op => op.classifiedType === 'scaffold' && op.type === 'query');
23
+ -%>
24
+ package <%= packageName %>.<%= moduleName %>.infrastructure.rest.controllers.<%= resourceNameCamel %>.<%= apiVersion %>;
25
+
26
+ <% if (hasCreate) { -%>
27
+ import <%= packageName %>.<%= moduleName %>.application.commands.Create<%= aggregateName %>Command;
28
+ <% } -%>
29
+ <% if (hasUpdate) { -%>
30
+ import <%= packageName %>.<%= moduleName %>.application.commands.Update<%= aggregateName %>Command;
31
+ <% } -%>
32
+ <% if (hasDelete) { -%>
33
+ import <%= packageName %>.<%= moduleName %>.application.commands.Delete<%= aggregateName %>Command;
34
+ <% } -%>
35
+ <% if (hasGetById) { -%>
36
+ import <%= packageName %>.<%= moduleName %>.application.queries.Get<%= aggregateName %>Query;
37
+ <% } -%>
38
+ <% if (hasFindAll) { -%>
39
+ import <%= packageName %>.<%= moduleName %>.application.queries.FindAll<%= aggregateName %>sQuery;
40
+ <% } -%>
41
+ <% customCmdUCs.forEach(function(op) { -%>
42
+ import <%= packageName %>.<%= moduleName %>.application.commands.<%= op.useCase %>Command;
43
+ <% }); -%>
44
+ <% customQueryUCs.forEach(function(op) { -%>
45
+ import <%= packageName %>.<%= moduleName %>.application.queries.<%= op.useCase %>Query;
46
+ <% }); -%>
47
+ <% transitionOps.forEach(function(op) { -%>
48
+ import <%= packageName %>.<%= moduleName %>.application.commands.<%= op.useCase %>Command;
49
+ <% }); -%>
50
+ <% subAddOps.forEach(function(op) { -%>
51
+ import <%= packageName %>.<%= moduleName %>.application.commands.<%= op.useCase %>Command;
52
+ <% }); -%>
53
+ <% subRemoveOps.forEach(function(op) { -%>
54
+ import <%= packageName %>.<%= moduleName %>.application.commands.<%= op.useCase %>Command;
55
+ <% }); -%>
56
+ <% findByOps.forEach(function(op) { -%>
57
+ import <%= packageName %>.<%= moduleName %>.application.queries.<%= op.useCase %>Query;
58
+ <% }); -%>
59
+ <% if (hasGetById || hasFindAll || hasFindBy) { -%>
60
+ import <%= packageName %>.<%= moduleName %>.application.dtos.<%= aggregateName %>ResponseDto;
61
+ <% } -%>
62
+ <% if (hasFindAll || hasFindBy) { -%>
63
+ import <%= packageName %>.shared.application.dtos.PagedResponse;
64
+ <% } -%>
65
+ import <%= packageName %>.shared.infrastructure.configurations.useCaseConfig.UseCaseMediator;
66
+
67
+ import io.swagger.v3.oas.annotations.Operation;
68
+ import io.swagger.v3.oas.annotations.tags.Tag;
69
+ import jakarta.validation.Valid;
70
+ import lombok.extern.slf4j.Slf4j;
71
+ import org.springframework.http.HttpStatus;
72
+ import org.springframework.web.bind.annotation.*;
73
+
74
+ @RestController
75
+ @RequestMapping("/api/<%= apiVersion %><%= basePath %>")
76
+ @Slf4j
77
+ @Tag(name = "<%= aggregateName %>", description = "<%= aggregateName %> Management API")
78
+ public class <%= controllerName %> {
79
+
80
+ private final UseCaseMediator useCaseMediator;
81
+
82
+ public <%= controllerName %>(UseCaseMediator useCaseMediator) {
83
+ this.useCaseMediator = useCaseMediator;
84
+ }
85
+ <% operations.forEach(function(op) { %>
86
+ /**
87
+ * <%= op.description || op.useCase %>
88
+ */
89
+ @<%- op.httpAnnotation %>(<% if (op.path && op.path !== '/') { %>"<%= op.path %>"<% } %>)
90
+ @ResponseStatus(<%= op.httpStatus %>)
91
+ @Operation(summary = "<%= op.description || op.useCase %>")
92
+ <% if (op.isStandard && op.standardType === 'create') { %>
93
+ public void <%= op.methodName %>(@Valid @RequestBody Create<%= aggregateName %>Command command) {
94
+ log.info("Creating <%= aggregateName %>: {}", command);
95
+ useCaseMediator.dispatch(command);
96
+ }
97
+ <% } else if (op.isStandard && op.standardType === 'getById') { %>
98
+ public <%= aggregateName %>ResponseDto <%= op.methodName %>(@PathVariable <%- idType %> id) {
99
+ log.info("Finding <%= aggregateName %> by id: {}", id);
100
+ return useCaseMediator.dispatch(new Get<%= aggregateName %>Query(id));
101
+ }
102
+ <% } else if (op.isStandard && op.standardType === 'findAll') { %>
103
+ public PagedResponse<<%= aggregateName %>ResponseDto> <%= op.methodName %>(
104
+ @RequestParam(defaultValue = "0") int page,
105
+ @RequestParam(defaultValue = "20") int size,
106
+ @RequestParam(defaultValue = "id") String sortBy,
107
+ @RequestParam(defaultValue = "ASC") String sortDirection) {
108
+ log.info("Finding all <%= aggregateName %>s — page={}, size={}, sortBy={}, sortDirection={}",
109
+ page, size, sortBy, sortDirection);
110
+ return useCaseMediator.dispatch(new FindAll<%= aggregateName %>sQuery(page, size, sortBy, sortDirection));
111
+ }
112
+ <% } else if (op.isStandard && op.standardType === 'delete') { %>
113
+ public void <%= op.methodName %>(@PathVariable <%- idType %> id) {
114
+ log.info("Deleting <%= aggregateName %> id: {}", id);
115
+ useCaseMediator.dispatch(new Delete<%= aggregateName %>Command(id));
116
+ }
117
+ <% } else if (op.isStandard && op.standardType === 'update') { %>
118
+ public void <%= op.methodName %>(
119
+ @PathVariable <%- idType %> id,
120
+ @RequestBody Update<%= aggregateName %>Command command) {
121
+ log.info("Updating <%= aggregateName %> id: {}", id);
122
+ useCaseMediator.dispatch(new Update<%= aggregateName %>Command(
123
+ id<% if (commandFields && commandFields.length > 0) { %>,<% } %>
124
+ <% (commandFields || []).forEach(function(field, idx) { %>
125
+ command.<%= field.name %>()<% if (idx < commandFields.length - 1 || (oneToManyRelationships && oneToManyRelationships.length > 0) || (oneToOneRelationships && oneToOneRelationships.length > 0)) { %>,<% } %>
126
+ <% }); %>
127
+ <% if (oneToManyRelationships && oneToManyRelationships.length > 0) { %>
128
+ <% oneToManyRelationships.forEach(function(rel, idx) { %>
129
+ command.<%= rel.fieldName %>()<% if (idx < oneToManyRelationships.length - 1 || (oneToOneRelationships && oneToOneRelationships.length > 0)) { %>,<% } %>
130
+ <% }); %>
131
+ <% } %>
132
+ <% if (oneToOneRelationships && oneToOneRelationships.length > 0) { %>
133
+ <% oneToOneRelationships.forEach(function(rel, idx) { %>
134
+ command.<%= rel.fieldName %>()<% if (idx < oneToOneRelationships.length - 1) { %>,<% } %>
135
+ <% }); %>
136
+ <% } %>
137
+ ));
138
+ }
139
+ <% } else if (op.classifiedType === 'transition') { %>
140
+ public void <%= op.methodName %>(@PathVariable <%- op.idType %> id) {
141
+ log.info("Handling <%= op.useCase %> for <%= aggregateName %> id: {}", id);
142
+ useCaseMediator.dispatch(new <%= op.useCase %>Command(id));
143
+ }
144
+ <% } else if (op.classifiedType === 'subEntityAdd') { %>
145
+ public void <%= op.methodName %>(@PathVariable <%- op.idType %> id, @Valid @RequestBody <%= op.useCase %>Command command) {
146
+ log.info("Handling <%= op.useCase %> for <%= aggregateName %> id: {}", id);
147
+ useCaseMediator.dispatch(new <%= op.useCase %>Command(id<% if (op.classification && op.classification.entityFields && op.classification.entityFields.length > 0) { %>, <% op.classification.entityFields.forEach(function(f, idx) { %>command.<%= f.name %>()<% if (idx < op.classification.entityFields.length - 1) { %>, <% } %><% }); %><% } %>));
148
+ }
149
+ <% } else if (op.classifiedType === 'subEntityRemove') { %>
150
+ public void <%= op.methodName %>(@PathVariable <%- op.idType %> id, @PathVariable String itemId) {
151
+ log.info("Handling <%= op.useCase %> for <%= aggregateName %> id: {}, itemId: {}", id, itemId);
152
+ useCaseMediator.dispatch(new <%= op.useCase %>Command(id, itemId));
153
+ }
154
+ <% } else if (op.classifiedType === 'findBy') { %>
155
+ public PagedResponse<<%= aggregateName %>ResponseDto> <%= op.methodName %>(
156
+ @RequestParam <%- op.classification.fieldJavaType %> <%= op.classification.fieldName %>,
157
+ @RequestParam(defaultValue = "0") int page,
158
+ @RequestParam(defaultValue = "20") int size,
159
+ @RequestParam(defaultValue = "id") String sortBy,
160
+ @RequestParam(defaultValue = "ASC") String sortDirection) {
161
+ log.info("Handling <%= op.useCase %> — <%= op.classification.fieldName %>={}, page={}, size={}", <%= op.classification.fieldName %>, page, size);
162
+ return useCaseMediator.dispatch(new <%= op.useCase %>Query(<%= op.classification.fieldName %>, page, size, sortBy, sortDirection));
163
+ }
164
+ <% } else if (op.type === 'command') { %>
165
+ // TODO: Review the fields declared in <%= op.useCase %>Command and adapt the method signature if needed
166
+ public void <%= op.methodName %>(<% if (op.hasPathVar) { %>@PathVariable <%- op.idType %> <%= op.pathVarName %>, <% } %>@Valid @RequestBody <%= op.useCase %>Command command) {
167
+ log.info("Handling <%= op.useCase %>: {}", command);
168
+ useCaseMediator.dispatch(command);
169
+ }
170
+ <% } else { %>
171
+ // TODO: Review the parameters in <%= op.useCase %>Query and replace Object with the actual return type
172
+ public Object <%= op.methodName %>(<% if (op.hasPathVar) { %>@PathVariable <%- op.idType %> <%= op.pathVarName %><% } %>) {
173
+ log.info("Handling <%= op.useCase %>");
174
+ return useCaseMediator.dispatch(new <%= op.useCase %>Query(<% if (op.hasPathVar) { %><%= op.pathVarName %><% } %>));
175
+ }
176
+ <% } -%>
177
+ <% }); %>
178
+ }
@@ -0,0 +1,17 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.queries;
2
+
3
+ import <%= packageName %>.shared.domain.interfaces.Query;
4
+ import <%= packageName %>.shared.application.dtos.PagedResponse;
5
+ import <%= packageName %>.<%= moduleName %>.application.dtos.<%= aggregateName %>ResponseDto;
6
+
7
+ /**
8
+ * <%= useCaseName %>Query
9
+ * Returns a paginated list of <%= aggregateName %>s filtered by <%= fieldName %>.
10
+ */
11
+ public record <%= useCaseName %>Query(
12
+ <%- fieldJavaType %> <%= fieldName %>,
13
+ int page,
14
+ int size,
15
+ String sortBy,
16
+ String sortDirection
17
+ ) implements Query<PagedResponse<<%= aggregateName %>ResponseDto>> {}
@@ -0,0 +1,57 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.usecases;
2
+
3
+ import <%= packageName %>.<%= moduleName %>.application.queries.<%= useCaseName %>Query;
4
+ import <%= packageName %>.<%= moduleName %>.application.dtos.<%= aggregateName %>ResponseDto;
5
+ import <%= packageName %>.<%= moduleName %>.application.mappers.<%= aggregateName %>ApplicationMapper;
6
+ import <%= packageName %>.<%= moduleName %>.domain.models.entities.<%= aggregateName %>;
7
+ import <%= packageName %>.<%= moduleName %>.domain.repositories.<%= aggregateName %>Repository;
8
+ import <%= packageName %>.shared.application.dtos.PagedResponse;
9
+ import <%= packageName %>.shared.domain.annotations.ApplicationComponent;
10
+ import <%= packageName %>.shared.domain.interfaces.QueryHandler;
11
+ import lombok.extern.slf4j.Slf4j;
12
+ import org.springframework.data.domain.Page;
13
+ import org.springframework.data.domain.PageRequest;
14
+ import org.springframework.data.domain.Pageable;
15
+ import org.springframework.data.domain.Sort;
16
+ import org.springframework.transaction.annotation.Transactional;
17
+
18
+ import java.util.List;
19
+
20
+ /**
21
+ * <%= useCaseName %>QueryHandler
22
+ * Returns paginated <%= aggregateName %>s filtered by <%= fieldName %>.
23
+ * Calls repository.<%= jpaMethodName %>(<%= fieldName %>, pageable).
24
+ */
25
+ @Slf4j
26
+ @ApplicationComponent
27
+ public class <%= useCaseName %>QueryHandler implements QueryHandler<<%= useCaseName %>Query, PagedResponse<<%= aggregateName %>ResponseDto>> {
28
+
29
+ private final <%= aggregateName %>Repository repository;
30
+ private final <%= aggregateName %>ApplicationMapper mapper;
31
+
32
+ public <%= useCaseName %>QueryHandler(<%= aggregateName %>Repository repository,
33
+ <%= aggregateName %>ApplicationMapper mapper) {
34
+ this.repository = repository;
35
+ this.mapper = mapper;
36
+ }
37
+
38
+ @Override
39
+ @Transactional(readOnly = true)
40
+ public PagedResponse<<%= aggregateName %>ResponseDto> handle(<%= useCaseName %>Query query) {
41
+ log.info("Handling <%= useCaseName %>Query — <%= fieldName %>={}, page={}, size={}",
42
+ query.<%= fieldName %>(), query.page(), query.size());
43
+
44
+ Sort sort = Sort.by(Sort.Direction.fromString(query.sortDirection()), query.sortBy());
45
+ Pageable pageable = PageRequest.of(query.page(), query.size(), sort);
46
+
47
+ Page<<%= aggregateName %>> page = repository.<%= jpaMethodName %>(query.<%= fieldName %>(), pageable);
48
+ List<<%= aggregateName %>ResponseDto> content = page.getContent().stream()
49
+ .map(mapper::toDto)
50
+ .toList();
51
+
52
+ log.info("<%= useCaseName %> retrieved — page={}/{}, totalElements={}",
53
+ page.getNumber() + 1, page.getTotalPages(), page.getTotalElements());
54
+
55
+ return PagedResponse.of(content, page.getNumber(), page.getSize(), page.getTotalElements());
56
+ }
57
+ }
@@ -0,0 +1,12 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.commands;
2
+
3
+ import <%= packageName %>.shared.domain.interfaces.Command;
4
+
5
+ /**
6
+ * <%= useCaseName %>Command
7
+ * TODO: Add the fields required by this use case and remove the placeholder.
8
+ */
9
+ public record <%= useCaseName %>Command(
10
+ // TODO: Replace with actual command fields
11
+ String id
12
+ ) implements Command {}
@@ -0,0 +1,43 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.usecases;
2
+
3
+ import <%= packageName %>.<%= moduleName %>.application.commands.<%= useCaseName %>Command;
4
+ import <%= packageName %>.<%= moduleName %>.domain.repositories.<%= aggregateName %>Repository;
5
+ import <%= packageName %>.shared.domain.annotations.ApplicationComponent;
6
+ import <%= packageName %>.shared.domain.interfaces.CommandHandler;
7
+ import lombok.extern.slf4j.Slf4j;
8
+ import org.springframework.transaction.annotation.Transactional;
9
+
10
+ /**
11
+ * <%= useCaseName %>CommandHandler
12
+ * TODO: Implement the business logic for the use case "<%= useCaseName %>".
13
+ *
14
+ * Suggested steps:
15
+ * 1. Find the aggregate root by ID via repository
16
+ * 2. Call the appropriate domain method (e.g. entity.someBusinessMethod())
17
+ * 3. Persist the result via repository.save(entity)
18
+ */
19
+ @Slf4j
20
+ @ApplicationComponent
21
+ public class <%= useCaseName %>CommandHandler implements CommandHandler<<%= useCaseName %>Command> {
22
+
23
+ private final <%= aggregateName %>Repository repository;
24
+
25
+ public <%= useCaseName %>CommandHandler(<%= aggregateName %>Repository repository) {
26
+ this.repository = repository;
27
+ }
28
+
29
+ @Override
30
+ @Transactional
31
+ public void handle(<%= useCaseName %>Command command) {
32
+ log.info("Handling <%= useCaseName %>Command: {}", command);
33
+
34
+ // TODO: Implement business logic
35
+ // Example:
36
+ // <%= aggregateName %> entity = repository.findById(command.id())
37
+ // .orElseThrow(() -> new EntityNotFoundException("<%= aggregateName %> not found"));
38
+ // entity.someBusinessMethod();
39
+ // repository.save(entity);
40
+
41
+ throw new UnsupportedOperationException("<%= useCaseName %> not yet implemented");
42
+ }
43
+ }
@@ -0,0 +1,12 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.queries;
2
+
3
+ import <%= packageName %>.shared.domain.interfaces.Query;
4
+
5
+ /**
6
+ * <%= useCaseName %>Query
7
+ * TODO: Add the parameters required by this query and replace the return type placeholder.
8
+ */
9
+ public record <%= useCaseName %>Query(
10
+ // TODO: Replace with actual query parameters
11
+ String id
12
+ ) implements Query<Object> {}
@@ -0,0 +1,40 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.usecases;
2
+
3
+ import <%= packageName %>.<%= moduleName %>.application.queries.<%= useCaseName %>Query;
4
+ import <%= packageName %>.<%= moduleName %>.domain.repositories.<%= aggregateName %>Repository;
5
+ import <%= packageName %>.shared.domain.annotations.ApplicationComponent;
6
+ import <%= packageName %>.shared.domain.interfaces.QueryHandler;
7
+ import lombok.extern.slf4j.Slf4j;
8
+
9
+ /**
10
+ * <%= useCaseName %>QueryHandler
11
+ * TODO: Implement the query logic for "<%= useCaseName %>".
12
+ *
13
+ * Suggested steps:
14
+ * 1. Read parameters from the query record
15
+ * 2. Retrieve data from the repository
16
+ * 3. Map the result to a DTO and return it
17
+ */
18
+ @Slf4j
19
+ @ApplicationComponent
20
+ public class <%= useCaseName %>QueryHandler implements QueryHandler<<%= useCaseName %>Query, Object> {
21
+
22
+ private final <%= aggregateName %>Repository repository;
23
+
24
+ public <%= useCaseName %>QueryHandler(<%= aggregateName %>Repository repository) {
25
+ this.repository = repository;
26
+ }
27
+
28
+ @Override
29
+ public Object handle(<%= useCaseName %>Query query) {
30
+ log.info("Handling <%= useCaseName %>Query: {}", query);
31
+
32
+ // TODO: Implement query logic
33
+ // Example:
34
+ // return repository.findById(query.id())
35
+ // .map(mapper::toDto)
36
+ // .orElseThrow(() -> new EntityNotFoundException("<%= aggregateName %> not found"));
37
+
38
+ throw new UnsupportedOperationException("<%= useCaseName %> not yet implemented");
39
+ }
40
+ }
@@ -0,0 +1,17 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.commands;
2
+
3
+ <% (imports || []).forEach(imp => { %>
4
+ <%- imp %>
5
+ <% }); %>
6
+ import <%= packageName %>.shared.domain.interfaces.Command;
7
+
8
+ /**
9
+ * <%= useCaseName %>Command
10
+ * Adds a new <%= entityName %> to the <%= aggregateName %> aggregate.
11
+ */
12
+ public record <%= useCaseName %>Command(
13
+ <%- idType %> id<% if (entityFields && entityFields.length > 0) { %>,<% } %>
14
+ <% (entityFields || []).forEach(function(field, idx) { %>
15
+ <%- field.javaType %> <%= field.name %><% if (idx < entityFields.length - 1) { %>,<% } %>
16
+ <% }); %>
17
+ ) implements Command {}
@@ -0,0 +1,43 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.usecases;
2
+
3
+ import <%= packageName %>.<%= moduleName %>.application.commands.<%= useCaseName %>Command;
4
+ import <%= packageName %>.<%= moduleName %>.domain.models.entities.<%= aggregateName %>;
5
+ import <%= packageName %>.<%= moduleName %>.domain.models.entities.<%= entityName %>;
6
+ import <%= packageName %>.<%= moduleName %>.domain.repositories.<%= aggregateName %>Repository;
7
+ import <%= packageName %>.shared.domain.annotations.ApplicationComponent;
8
+ import <%= packageName %>.shared.domain.customExceptions.NotFoundException;
9
+ import <%= packageName %>.shared.domain.interfaces.CommandHandler;
10
+ import lombok.extern.slf4j.Slf4j;
11
+ import org.springframework.transaction.annotation.Transactional;
12
+
13
+ /**
14
+ * <%= useCaseName %>CommandHandler
15
+ * Loads the <%= aggregateName %> aggregate and adds a new <%= entityName %> to it.
16
+ *
17
+ * The aggregate root must expose: <%= addMethodName %>(<%= entityName %> item)
18
+ */
19
+ @Slf4j
20
+ @ApplicationComponent
21
+ public class <%= useCaseName %>CommandHandler implements CommandHandler<<%= useCaseName %>Command> {
22
+
23
+ private final <%= aggregateName %>Repository repository;
24
+
25
+ public <%= useCaseName %>CommandHandler(<%= aggregateName %>Repository repository) {
26
+ this.repository = repository;
27
+ }
28
+
29
+ @Override
30
+ @Transactional
31
+ public void handle(<%= useCaseName %>Command command) {
32
+ log.info("Handling <%= useCaseName %>Command for <%= aggregateName %> id: {}", command.id());
33
+
34
+ <%= aggregateName %> entity = repository.findById(command.id())
35
+ .orElseThrow(() -> new NotFoundException("<%= aggregateName %> not found with id: " + command.id()));
36
+
37
+ <%= entityName %> item = new <%= entityName %>(<% (entityFields || []).forEach(function(field, idx) { %>command.<%= field.name %>()<% if (idx < entityFields.length - 1) { %>, <% } %><% }); %>);
38
+ entity.<%= addMethodName %>(item);
39
+ repository.save(entity);
40
+
41
+ log.info("<%= entityName %> added to <%= aggregateName %> id: {}", command.id());
42
+ }
43
+ }
@@ -0,0 +1,9 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.commands;
2
+
3
+ import <%= packageName %>.shared.domain.interfaces.Command;
4
+
5
+ /**
6
+ * <%= useCaseName %>Command
7
+ * Removes the <%= entityName %> identified by itemId from the <%= aggregateName %> aggregate.
8
+ */
9
+ public record <%= useCaseName %>Command(<%- idType %> id, String itemId) implements Command {}
@@ -0,0 +1,42 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.usecases;
2
+
3
+ import <%= packageName %>.<%= moduleName %>.application.commands.<%= useCaseName %>Command;
4
+ import <%= packageName %>.<%= moduleName %>.domain.models.entities.<%= aggregateName %>;
5
+ import <%= packageName %>.<%= moduleName %>.domain.repositories.<%= aggregateName %>Repository;
6
+ import <%= packageName %>.shared.domain.annotations.ApplicationComponent;
7
+ import <%= packageName %>.shared.domain.customExceptions.NotFoundException;
8
+ import <%= packageName %>.shared.domain.interfaces.CommandHandler;
9
+ import lombok.extern.slf4j.Slf4j;
10
+ import org.springframework.transaction.annotation.Transactional;
11
+
12
+ /**
13
+ * <%= useCaseName %>CommandHandler
14
+ * Loads the <%= aggregateName %> aggregate and removes the <%= entityName %> by itemId.
15
+ *
16
+ * The aggregate root must expose: <%= removeMethodName %>(String itemId)
17
+ */
18
+ @Slf4j
19
+ @ApplicationComponent
20
+ public class <%= useCaseName %>CommandHandler implements CommandHandler<<%= useCaseName %>Command> {
21
+
22
+ private final <%= aggregateName %>Repository repository;
23
+
24
+ public <%= useCaseName %>CommandHandler(<%= aggregateName %>Repository repository) {
25
+ this.repository = repository;
26
+ }
27
+
28
+ @Override
29
+ @Transactional
30
+ public void handle(<%= useCaseName %>Command command) {
31
+ log.info("Handling <%= useCaseName %>Command for <%= aggregateName %> id: {}, itemId: {}",
32
+ command.id(), command.itemId());
33
+
34
+ <%= aggregateName %> entity = repository.findById(command.id())
35
+ .orElseThrow(() -> new NotFoundException("<%= aggregateName %> not found with id: " + command.id()));
36
+
37
+ entity.<%= removeMethodName %>(command.itemId());
38
+ repository.save(entity);
39
+
40
+ log.info("<%= entityName %> id: {} removed from <%= aggregateName %> id: {}", command.itemId(), command.id());
41
+ }
42
+ }
@@ -0,0 +1,9 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.commands;
2
+
3
+ import <%= packageName %>.shared.domain.interfaces.Command;
4
+
5
+ /**
6
+ * <%= useCaseName %>Command
7
+ * Triggers the '<%= domainMethod %>' state transition on the <%= aggregateName %> aggregate.
8
+ */
9
+ public record <%= useCaseName %>Command(<%- idType %> id) implements Command {}
@@ -0,0 +1,39 @@
1
+ package <%= packageName %>.<%= moduleName %>.application.usecases;
2
+
3
+ import <%= packageName %>.<%= moduleName %>.application.commands.<%= useCaseName %>Command;
4
+ import <%= packageName %>.<%= moduleName %>.domain.models.entities.<%= aggregateName %>;
5
+ import <%= packageName %>.<%= moduleName %>.domain.repositories.<%= aggregateName %>Repository;
6
+ import <%= packageName %>.shared.domain.annotations.ApplicationComponent;
7
+ import <%= packageName %>.shared.domain.customExceptions.NotFoundException;
8
+ import <%= packageName %>.shared.domain.interfaces.CommandHandler;
9
+ import lombok.extern.slf4j.Slf4j;
10
+ import org.springframework.transaction.annotation.Transactional;
11
+
12
+ /**
13
+ * <%= useCaseName %>CommandHandler
14
+ * Loads the <%= aggregateName %> aggregate and calls entity.<%= domainMethod %>() to execute the transition.
15
+ */
16
+ @Slf4j
17
+ @ApplicationComponent
18
+ public class <%= useCaseName %>CommandHandler implements CommandHandler<<%= useCaseName %>Command> {
19
+
20
+ private final <%= aggregateName %>Repository repository;
21
+
22
+ public <%= useCaseName %>CommandHandler(<%= aggregateName %>Repository repository) {
23
+ this.repository = repository;
24
+ }
25
+
26
+ @Override
27
+ @Transactional
28
+ public void handle(<%= useCaseName %>Command command) {
29
+ log.info("Handling <%= useCaseName %>Command for id: {}", command.id());
30
+
31
+ <%= aggregateName %> entity = repository.findById(command.id())
32
+ .orElseThrow(() -> new NotFoundException("<%= aggregateName %> not found with id: " + command.id()));
33
+
34
+ entity.<%= domainMethod %>();
35
+ repository.save(entity);
36
+
37
+ log.info("<%= aggregateName %> id: {} transitioned via <%= domainMethod %>()", command.id());
38
+ }
39
+ }