Skip to content

Commit

Permalink
adds support for rich 'aggregate' modeling (refactoring)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivangsa committed Mar 26, 2024
1 parent d8619a3 commit 65e555c
Show file tree
Hide file tree
Showing 29 changed files with 560 additions and 343 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,50 @@ public class BackendApplicationDefaultHelpers {
this.generator = generator;
}

public String logMethodCall(Map method, Options options) {
var zdl = options.get("zdl");
var methodName = (String) method.get("name");
var parameters = ZDLJavaSignatureUtils.methodParametersCallSignature(method, (Map) zdl, generator.inputDTOSuffix);
var parameterPlaceHolders = Arrays.stream(parameters.split(", ")).map(p -> "{}").collect(Collectors.joining(" "));
if(parameters.isEmpty()) {
return String.format("log.debug(\"Request %s\");", methodName);
}
return String.format("log.debug(\"Request %s: %s\", %s);", methodName, parameterPlaceHolders, parameters);
}

public Map findAggregateCommandsForMethod(Map method, Options options) {
var zdl = options.get("zdl");
var aggregatesCommandsForMethod = ZDLFindUtils.findAggregateCommandsForMethod((Map) zdl, method);
if(aggregatesCommandsForMethod.isEmpty()) {
return Map.of("templateFile", "aggregates-void-methodBody");
}
if(aggregatesCommandsForMethod.size() == 1) {
var aggregate = JSONPath.get(aggregatesCommandsForMethod, "$[0].aggregate");
var entity = JSONPath.get(aggregatesCommandsForMethod, "$[0].entity");
var command = JSONPath.get(aggregatesCommandsForMethod, "$[0].command");
var crudMethod = JSONPath.get(aggregatesCommandsForMethod, "$[0].crudMethod");
if(aggregate != null && command != null) {
return Map.of("templateFile", "aggregates-commands-methodBody", "aggregatesCommandsForMethod", aggregatesCommandsForMethod);
}
if(aggregate != null && crudMethod != null) {
return Map.of("templateFile", "aggregates-crud-methodBody", "aggregatesCommandsForMethod", aggregatesCommandsForMethod);
}
if(entity != null && crudMethod != null) {
return Map.of("templateFile", "entities-crud-methodBody", "entity", entity, "crudMethod", crudMethod);
}
if(entity != null) {
return Map.of("templateFile", "entities-methodBody", "entity", entity);
}
}
return Map.of("templateFile", "aggregates-commands-methodBody", "aggregatesCommandsForMethod", aggregatesCommandsForMethod);
}
public boolean isWriteMethod(Map method, Options options) {
var methodName = (String) method.get("name");
var hasId = method.get("paramId") != null;
return hasId || methodName.startsWith("create") || methodName.startsWith("update") || methodName.startsWith("delete");
}

@Deprecated
public boolean isCrudMethod(String crudMethodPrefix, Options options) {
var entity = (Map<String, Object>) options.hash("entity");
var entityName = (String) entity.get("name");
Expand Down Expand Up @@ -69,12 +113,11 @@ public String findEntityAggregate(String entityName, Options options) {
return aggregateNames.isEmpty()? null : (String) aggregateNames.get(0);
}

public Collection<String> findServiceInputs(Map entity, Options options) {
public Collection<String> findServiceInputs(Map service, Options options) {
var zdl = options.get("zdl");
var aggregateName = (String) entity.get("name");
var inputDTOSuffix = (String) options.get("inputDTOSuffix");
Set<String> inputs = new HashSet<String>();
inputs.addAll(JSONPath.get(zdl, "$.services[*][?('" + aggregateName + "' in @.aggregates)].methods[*].parameter"));
inputs.addAll(JSONPath.get(service, "$.methods[*].parameter"));
// inputs.addAll(JSONPath.get(zdl, "$.services[*][?('" + aggregateName + "' in @.aggregates)].methods[*].returnType"));
// inputs.add(aggregateName + inputDTOSuffix);
inputs = inputs.stream().filter(Objects::nonNull).collect(Collectors.toSet());
Expand All @@ -86,12 +129,12 @@ public Collection<String> findServiceInputs(Map entity, Options options) {
return inputs;
}

public Collection<String> findAggregateOutputs(Map aggregate, Options options) {
public Collection<String> findServiceOutputs(Map service, Options options) {
var zdl = options.get("zdl");
var aggregateName = (String) aggregate.get("name");
var aggregates = (List<String>) service.get("aggregates");
Set<String> outputs = new HashSet<String>();
outputs.addAll(JSONPath.get(zdl, "$.services[*][?('" + aggregateName + "' in @.aggregates)].methods[*].returnType"));
outputs = outputs.stream().filter(input -> input != null && !aggregateName.equals(input)).collect(Collectors.toSet());
outputs.addAll(JSONPath.get(service, "$.methods[*].returnType"));
outputs = outputs.stream().filter(input -> input != null && !aggregates.contains(input)).collect(Collectors.toSet());
return outputs;
}

Expand Down Expand Up @@ -209,20 +252,22 @@ public String wrapWithMapper(Map<String, Object> entity, Options options) {
return "";
}
var returnTypeIsArray = (Boolean) method.getOrDefault("returnTypeIsArray", false);
var isReturnTypeOptional = (Boolean) method.getOrDefault("returnTypeIsOptional", false);
var instanceName = returnTypeIsArray? entity.get("instanceNamePlural") : entity.get("instanceName");
var serviceInstanceName = NamingUtils.asInstanceName((String) method.get("serviceName"));
if (Objects.equals(entity.get("name"), returnType.get("name"))) {
if(JSONPath.get(method, "options.paginated", false)) {
return "page";
}
// if(JSONPath.get(method, "options.paginated", false)) {
// return (String) instanceName;
// }
return (String) instanceName;
} else {
if(returnTypeIsArray) {
if(JSONPath.get(method, "options.paginated", false)) {
return String.format("%sMapper.as%sPage(%s)", instanceName, returnType.get("className"), "page");
return String.format("%sMapper.as%sPage(%s)", serviceInstanceName, returnType.get("className"), instanceName);
}
return String.format("%sMapper.as%sList(%s)", instanceName, returnType.get("className"), instanceName);
return String.format("%sMapper.as%sList(%s)", serviceInstanceName, returnType.get("className"), instanceName);
} else {
return String.format("%sMapper.as%s(%s)", instanceName, returnType.get("className"), instanceName);
return String.format("%sMapper.as%s(%s)", serviceInstanceName, returnType.get("className"), instanceName);
}
}
}
Expand All @@ -247,7 +292,14 @@ public String javaType(Map field, Options options) {

public Object findEntity(String entityName, Options options) {
var zdl = options.get("zdl");
return JSONPath.get(zdl, "entities." + entityName);
var entity = JSONPath.get(zdl, "$.allEntitiesAndEnums." + entityName);
if("entities".equals(JSONPath.get(entity, "type"))) {
return entity;
}
if("aggregates".equals(JSONPath.get(entity, "type"))) {
return findEntity(JSONPath.get(entity, "$.aggregateRoot"), options);
}
return null;
}

public String inputDTOSuffix(Object entity, Options options) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ protected ZDLProjectTemplates configureProjectTemplates() {

ts.addTemplate(ts.serviceTemplates, "src/main/java", "core/inbound/Service.java",
"{{asPackageFolder inboundPackage}}/{{service.name}}.java", JAVA, null, false);
ts.addTemplate(ts.serviceTemplates, "src/main/java", "core/implementation/{{persistence}}/{{style}}/ServiceImpl.java",
ts.addTemplate(ts.serviceTemplates, "src/main/java", "core/implementation/{{style}}/ServiceImpl.java",
"{{asPackageFolder coreImplementationPackage}}/{{service.name}}Impl.java", JAVA, null, true);
ts.addTemplate(ts.singleTemplates, "src/main/java", "core/implementation/mappers/BaseMapper.java",
"{{asPackageFolder coreImplementationPackage}}/mappers/BaseMapper.java", JAVA, null, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected ZDLProjectTemplates configureProjectTemplates() {

ts.addTemplate(ts.serviceTemplates, "src/main/java", "core/inbound/Service.java", "{{mavenModulesPrefix}}-domain",
"{{asPackageFolder inboundPackage}}/{{service.name}}.java", JAVA, null, false);
ts.addTemplate(ts.serviceTemplates, "src/main/java", "core/implementation/{{persistence}}/{{style}}/ServiceImpl.java", "{{mavenModulesPrefix}}-impl",
ts.addTemplate(ts.serviceTemplates, "src/main/java", "core/implementation/{{style}}/ServiceImpl.java", "{{mavenModulesPrefix}}-impl",
"{{asPackageFolder coreImplementationPackage}}/{{service.name}}Impl.java", JAVA, null, true);
ts.addTemplate(ts.serviceTemplates, "src/main/java","core/implementation/mappers/ServiceMapper.java", "{{mavenModulesPrefix}}-impl",
"{{asPackageFolder coreImplementationPackage}}/mappers/{{service.name}}Mapper.java", JAVA, null, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {{entitiesPackage}}.*;
import {{inboundPackage}}.*;
import {{inboundDtosPackage}}.*;
import {{coreImplementationPackage}}.mappers.*;
import {{outboundPackage}}.jpa.*;
import {{outboundPackage}}.{{persistence}}.*;
{{#if includeEmitEventsImplementation}}
import {{outboundEventsPackage}}.*;
{{/if}}
Expand Down Expand Up @@ -74,9 +74,9 @@ public class {{service.name}}Impl implements {{service.name}} {
{{/unless~}}

{{#each service.methods as |method|}}
{{~> (partial '../../partials/jpaMethodAnnotations')~}}
{{~> (partial '../../partials/serviceMethodSignature')}} {
{{~> (partial '../../partials/jpaMethodBody')~}}
{{~> (partial '../partials/' persistence '/methodAnnotations')~}}
{{~> (partial '../partials/serviceMethodSignature')}} {
{{~> (partial '../partials/' persistence '/methodBody')~}}
}
{{/each}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import org.springframework.data.domain.Page;
public interface {{service.name}}Mapper {

{{service.name}}Mapper INSTANCE = Mappers.getMapper({{service.name}}Mapper.class);
{{~#each service.entityNames as |entityName|}}
{{~#each service.aggregates as |entityName|}}
{{~assign "entity" (findEntity entityName)}}

{{~#each (findServiceInputs entity) as |input|}}
{{~#each (findServiceInputs service) as |input|}}
{{~#if (not (eq entity.className input))}}
// {{entity.className}} as{{entity.className}}({{mapperInputSignature input}});
{{~/if}}
Expand All @@ -27,7 +27,7 @@ public interface {{service.name}}Mapper {
{{entity.className}} update(@MappingTarget {{entity.className}} entity, {{mapperInputSignature input}});
{{~/each}}

{{~#each (findAggregateOutputs entity) as |output|}}
{{~#each (findServiceOutputs service) as |output|}}
{{output}} as{{output}}({{entity.className}} entity);
List<{{output}}> as{{output}}List(List<{{entity.className}}> entity);
default Page<{{output}}> as{{output}}Page(Page<{{entity.className}}> page) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// aggregates commands
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// void
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{{assign 'entity' aggregateCommandsForMethod.entity }}
{{~#if (isCrudMethod 'create' method=method entity=entity )}}
log.debug("[CRUD] Request to save {{entity.className}}: {}", input);
var {{entity.instanceName}} = {{asInstanceName service.name}}Mapper.update(new {{entity.className}}(), {{{mapperInputCallSignature method.parameter}}});
{{entity.instanceName}} = {{entity.instanceName}}Repository.save({{entity.instanceName}});
// TODO: you may need to reload the entity here to fetch relationships 'mapped by id'
{{~> (partial '../withEvents')}}
return {{wrapWithMapper entity}};
{{~else if (isCrudMethod 'update' method=method entity=entity )}}
log.debug("[CRUD] Request to update {{entity.className}}: {}", input);
var {{entity.instanceName}} = {{entity.instanceName}}Repository.findById(id);
// saving is unnecessary: https://vladmihalcea.com/best-spring-data-jparepository/
{{entity.instanceName}} = {{entity.instanceName}}.map(existing{{entity.instanceName}} -> {{asInstanceName service.name}}Mapper.update(existing{{entity.instanceName}}, {{{mapperInputCallSignature method.parameter}}}));
{{~> (partial '../withEvents')}}
return {{wrapWithMapper entity}};
{{~else if (isCrudMethod 'list' method=method entity=entity )}}
{{~#if method.options.paginated}}
log.debug("[CRUD] Request list of {{entity.classNamePlural}}: {}", pageable);
var {{entity.instanceNamePlural}} = {{entity.instanceName}}Repository.findAll(pageable);
return {{wrapWithMapper entity}};
{{~else}}
log.debug("Request list of {{entity.classNamePlural}}");
var {{entity.instanceNamePlural}} = {{entity.instanceName}}Repository.findAll();
return {{wrapWithMapper entity}};
{{~/if}}
{{~else if (isCrudMethod 'search' method=method entity=entity )}}
log.debug("[CRUD] Request to search {{entity.classNamePlural}}: {} - {}", input, pageable);
// TODO implement this search by criteria
var {{entity.instanceNamePlural}} = {{entity.instanceName}}Repository.findAll(pageable);
return {{wrapWithMapper entity}};
{{~else if (isCrudMethod 'get' method=method entity=entity )}}
log.debug("[CRUD] Request to get {{entity.className}} : {}", id);
var {{entity.instanceName}} = {{entity.instanceName}}Repository.findById(id);
return {{wrapWithMapper entity}};
{{~else if (isCrudMethod 'delete' method=method entity=entity )}}
log.debug("[CRUD] Request to delete {{entity.className}} : {}", id);
{{entity.instanceName}}Repository.deleteById(id);
{{~> (partial '../withEvents')}}
{{~/if}}

Loading

0 comments on commit 65e555c

Please sign in to comment.