Skip to content

Commit

Permalink
refactors domain Events for moduliths
Browse files Browse the repository at this point in the history
  • Loading branch information
ivangsa committed Jan 19, 2025
1 parent 8483d71 commit 10c4626
Show file tree
Hide file tree
Showing 16 changed files with 168 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public class BackendDefaultApplicationGenerator extends AbstractZDLProjectGenera

public String configPackage = "{{basePackage}}.config";
public String entitiesPackage = "{{basePackage}}.core.domain";
public String domainEventsPackage = "{{basePackage}}.core.domain.events";
public String inboundPackage = "{{basePackage}}.core.inbound";
public String inboundDtosPackage = "{{basePackage}}.core.inbound.dtos";
public String outboundPackage = "{{basePackage}}.core.outbound";
Expand All @@ -60,8 +59,12 @@ public class BackendDefaultApplicationGenerator extends AbstractZDLProjectGenera
public String infrastructureRepositoryPackage = "{{basePackage}}.infrastructure.{{persistence}}";
public String adaptersPackage = "{{basePackage}}.adapters";

public String outboundEventsModelPackage = "{{basePackage}}.core.outbound.events.dtos";
// domain events
public String domainEventsPackage = "{{basePackage}}.core.domain.events";
public String outboundEventsPackage = "{{basePackage}}.core.outbound.events";
public String infrastructureEventsPackage = "{{basePackage}}.infrastructure.events";
// asyncapi events
public String outboundEventsModelPackage = "{{basePackage}}.core.outbound.events.dtos";


@DocumentedOption(description = "Entities to generate code for")
Expand All @@ -79,8 +82,8 @@ public class BackendDefaultApplicationGenerator extends AbstractZDLProjectGenera
@DocumentedOption(description = "Use @Getter and @Setter annotations from Lombok")
public boolean useLombok = false;

@DocumentedOption(description = "Whether to add IEntityEventProducer interfaces as service dependencies. Depends on the naming convention of zenwave-asyncapi plugin to work.")
public boolean includeEmitEventsImplementation = false;
@DocumentedOption(description = "Whether to add AsyncAPI/ApplicationEventPublisher as service dependencies. Depends on the naming convention of zenwave-asyncapi plugin to work.")
public boolean includeEmitEventsImplementation = true;

@DocumentedOption(description = "Controls whether to add a read/write relationship by id when mapping relationships between aggregate (not recommended) keeping the relationship by object readonly.")
public boolean addRelationshipsById = false;
Expand Down Expand Up @@ -109,6 +112,7 @@ protected boolean is(Map<String, Object> model, String... annotations) {
protected Function<Map<String, Object>, Boolean> skipEntityInput = (model) -> inputDTOSuffix == null || inputDTOSuffix.isEmpty();

protected Function<Map<String, Object>, Boolean> skipEvents = (model) -> !includeEmitEventsImplementation;
protected Function<Map<String, Object>, Boolean> skipEventsBus = (model) -> ((Collection) model.get("events")).isEmpty();
protected Function<Map<String, Object>, Boolean> skipInput = (model) -> is(model, "inline");
@Override
protected ZDLProjectTemplates configureProjectTemplates() {
Expand Down Expand Up @@ -166,6 +170,13 @@ protected ZDLProjectTemplates configureProjectTemplates() {
ts.addTemplate(ts.allServicesTemplates, "src/test/java", "config/ServicesInMemoryConfig.java",
"{{asPackageFolder configPackage}}/ServicesInMemoryConfig.java", JAVA, null, true);

ts.addTemplate(ts.allEventsTemplates, "src/main/java", "core/outbound/events/EventPublisher.java",
"{{asPackageFolder outboundEventsPackage}}/EventPublisher.java", JAVA, skipEventsBus, false);
ts.addTemplate(ts.allEventsTemplates, "src/main/java", "infrastructure/events/DefaultEventPublisher.java",
"{{asPackageFolder infrastructureEventsPackage}}/DefaultEventPublisher.java", JAVA, skipEventsBus, false);
ts.addTemplate(ts.allEventsTemplates, "src/test/java", "infrastructure/events/InMemoryEventPublisher.java",
"{{asPackageFolder infrastructureEventsPackage}}/InMemoryEventPublisher.java", JAVA, skipEventsBus, false);

ts.addTemplate(ts.singleTemplates, "src/test/java", "config/TestDataLoader-{{persistence}}.java",
"{{asPackageFolder configPackage}}/TestDataLoader.java", JAVA, null, true);
ts.addTemplate(ts.singleTemplates, "src/test/java", "config/DockerComposeInitializer-{{persistence}}.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ protected ZDLProjectTemplates configureProjectTemplates() {
ts.addTemplate(ts.allServicesTemplates, "src/test/java", "config/ServicesInMemoryConfig.java", "{{mavenModulesPrefix}}-core-impl",
"{{asPackageFolder configPackage}}/ServicesInMemoryConfig.java", JAVA, null, true);

ts.addTemplate(ts.allEventsTemplates, "src/main/java", "core/outbound/events/EventPublisher.java", "{{mavenModulesPrefix}}-core-impl",
"{{asPackageFolder outboundEventsPackage}}/EventPublisher.java", JAVA, skipEventsBus, false);
ts.addTemplate(ts.allEventsTemplates, "src/main/java", "infrastructure/events/DefaultEventPublisher.java", "{{mavenModulesPrefix}}-core-impl",
"{{asPackageFolder infrastructureEventsPackage}}/DefaultEventPublisher.java", JAVA, skipEventsBus, false);
ts.addTemplate(ts.allEventsTemplates, "src/test/java", "infrastructure/events/InMemoryEventPublisher.java", "{{mavenModulesPrefix}}-core-impl",
"{{asPackageFolder infrastructureEventsPackage}}/InMemoryEventPublisher.java", JAVA, skipEventsBus, false);

ts.addTemplate(ts.singleTemplates, "src/test/java", "config/TestDataLoader-{{persistence}}.java", "{{mavenModulesPrefix}}-core-impl",
"{{asPackageFolder configPackage}}/TestDataLoader.java", JAVA, null, true);
ts.addTemplate(ts.singleTemplates, "src/test/java", "config/DockerComposeInitializer-{{persistence}}.java", "{{mavenModulesPrefix}}-core-impl",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ public class BackendSimpleDomainPackagingApplicationGenerator extends BackendDef
{
configPackage = "{{basePackage}}.config";
entitiesPackage = "{{basePackage}}.model";
domainEventsPackage = "{{basePackage}}.events";
inboundPackage = "{{basePackage}}";
inboundDtosPackage = "{{basePackage}}.dtos";
outboundPackage = "{{basePackage}}";
Expand All @@ -49,8 +48,12 @@ public class BackendSimpleDomainPackagingApplicationGenerator extends BackendDef
infrastructureRepositoryPackage = "{{basePackage}}";
adaptersPackage = "{{basePackage}}";

outboundEventsModelPackage = "{{basePackage}}.events.dtos";
// domain events
domainEventsPackage = "{{basePackage}}.events";
outboundEventsPackage = "{{basePackage}}.events";
infrastructureEventsPackage = "{{basePackage}}.events";
// asyncapi events
outboundEventsModelPackage = "{{basePackage}}.events.dtos";
}

@Override
Expand Down Expand Up @@ -109,6 +112,14 @@ protected ZDLProjectTemplates configureProjectTemplates() {
ts.addTemplate(ts.allServicesTemplates, "src/test/java", "config/ServicesInMemoryConfig.java",
"{{asPackageFolder configPackage}}/ServicesInMemoryConfig.java", JAVA, null, true);

ts.addTemplate(ts.allEventsTemplates, "src/main/java", "core/outbound/events/EventPublisher.java",
"{{asPackageFolder outboundEventsPackage}}/EventPublisher.java", JAVA, skipEventsBus, false);
ts.addTemplate(ts.allEventsTemplates, "src/main/java", "infrastructure/events/DefaultEventPublisher.java",
"{{asPackageFolder infrastructureEventsPackage}}/DefaultEventPublisher.java", JAVA, skipEventsBus, false);
ts.addTemplate(ts.allEventsTemplates, "src/test/java", "infrastructure/events/InMemoryEventPublisher.java",
"{{asPackageFolder infrastructureEventsPackage}}/InMemoryEventPublisher.java", JAVA, skipEventsBus, false);


ts.addTemplate(ts.singleTemplates, "src/test/java", "config/TestDataLoader-{{persistence}}.java",
"{{asPackageFolder configPackage}}/TestDataLoader.java", JAVA, null, true);
ts.addTemplate(ts.singleTemplates, "src/test/java", "config/DockerComposeInitializer-{{persistence}}.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import {{outboundRepositoryPackage}}.*;
{{~#if (and includeEmitEventsImplementation (needsEventsProducer service))}}
import {{outboundEventsPackage}}.*;
{{~/if}}
{{~#if (and includeEmitEventsImplementation (needsEventBus service))}}
import {{outboundEventsPackage}}.*;
{{~/if}}
{{~#if (includeDomainEvents service)}}
import {{domainEventsPackage}}.*;
{{~/if}}
Expand All @@ -24,9 +27,6 @@ import org.springframework.data.domain.Pageable;
import java.util.concurrent.CompletableFuture;
import org.springframework.scheduling.annotation.Async;
{{~/if}}
{{~#if (includeEmitEventsImplementation service)}}
import org.springframework.context.ApplicationEventPublisher;
{{~/if}}
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -55,7 +55,9 @@ public class {{service.name}}Impl implements {{service.name}} {
{{~#if (needsEventsProducer service)}}
private final {{eventsProducerInterface service.name}} eventsProducer;
{{~/if}}
private final ApplicationEventPublisher applicationEventPublisher;
{{~#if (needsEventBus service)}}
private final EventPublisher eventPublisher;
{{~/if}}
{{/if}}

{{#unless useLombok~}}
Expand All @@ -65,15 +67,15 @@ public class {{service.name}}Impl implements {{service.name}} {
public {{service.name}}Impl({{#joinWithTemplate service.entities delimiter=", "}}{{#unless (skipEntityRepository this)}}{{className}}Repository {{instanceName}}Repository{{/unless}}{{/joinWithTemplate}}
{{~#if (includeEmitEventsImplementation service)}}
{{~#if (needsEventsProducer service)}}, {{eventsProducerInterface service.name}} eventsProducer{{/if~}}
, ApplicationEventPublisher applicationEventPublisher
{{~#if (needsEventBus service)}}, EventPublisher eventPublisher{{/if~}}
{{~/if~}}
) {
{{~#joinWithTemplate service.entities ~}}
{{#unless (skipEntityRepository this)}}this.{{instanceName}}Repository = {{instanceName}}Repository;{{/unless}}
{{~/joinWithTemplate~}}
{{~#if (includeEmitEventsImplementation service)}}
this.eventsProducer = eventsProducer;
this.applicationEventPublisher = applicationEventPublisher;
{{~#if (needsEventsProducer service)}}this.eventsProducer = eventsProducer;{{/if}}
{{~#if (needsEventBus service)}}this.eventPublisher = eventPublisher;{{/if}}
{{~/if}}
}
{{/unless~}}
Expand All @@ -99,7 +101,7 @@ public class {{service.name}}Impl implements {{service.name}} {
// eventsProducer.{{operationNameForEvent event.name}}(eventsMapper.as{{event.name}}(({{event.className}}) event));
{{~/if}}
{{~else}}
applicationEventPublisher.publishEvent(event);
eventPublisher.on{{event.className}}(({{event.className}}) event);
{{~/if}}
}
{{/each}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{{~#if method.withEvents}}
{{~#if includeEmitEventsImplementation}}
{{~#if method.returnTypeIsOptional}} if({{entity.instanceName}}.isPresent()) { {{~/if}}
{{~#if method.returnTypeIsOptional}} if({{_entity.instanceName}}.isPresent()) { {{~/if}}
// emit events
{{~#each (methodEvents method) as |event|}}
{{~#if (methodEntity method)}}
var {{asInstanceName event.name}} = eventsMapper.as{{event.name}}({{entity.instanceName}}{{#if method.returnTypeIsOptional~}} .get() {{~/if}});
var {{asInstanceName event.name}} = eventsMapper.as{{event.name}}({{_entity.instanceName}}{{#if method.returnTypeIsOptional~}} .get() {{~/if}});
{{~else}}
var {{asInstanceName event.name}} = eventsMapper.as{{event.name}}({{{methodParametersCallSignature method}}});
{{~/if}}
{{~#if event.options.asyncapi}}
eventsProducer.{{operationNameForEvent event.name}}({{asInstanceName event.name}});
{{~else}}
applicationEventPublisher.publishEvent({{asInstanceName event.name}});
eventPublisher.on{{event.className}}({{asInstanceName event.name}});
{{~/if}}
{{~/each}}
{{~#if method.returnTypeIsOptional}} } {{~/if}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package {{outboundEventsPackage}};

import {{domainEventsPackage}}.*;
import {{entitiesPackage}}.*;

public interface EventPublisher {

{{#each events as |event|}}
void on{{event.className}}({{event.className}} event);
{{/each}}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package {{infrastructureEventsPackage}};

import {{domainEventsPackage}}.*;
import {{entitiesPackage}}.*;
import {{outboundEventsPackage}}.EventPublisher;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
@lombok.RequiredArgsConstructor
public class DefaultEventPublisher implements EventPublisher {

private final ApplicationEventPublisher applicationEventPublisher;

{{#each events as |event|}}
public void on{{event.className}}({{event.className}} event) {
applicationEventPublisher.publishEvent(event);
}
{{/each}}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import {{outboundEventsPackage}}.*;
{{#if (includeDomainEvents service)}}
import {{domainEventsPackage}}.*;
{{/if}}
import org.springframework.context.ApplicationEventPublisher;
{{~#if (needsEventBus service)}}
import {{infrastructureEventsPackage}}.*;
{{~/if}}
{{/if}}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -34,12 +36,9 @@ public class ServicesInMemoryConfig extends RepositoriesInMemoryConfig {
{{~#if (needsEventsProducer service)}}
protected final EventsProducerInMemoryContext eventsProducerInMemoryContext = new EventsProducerInMemoryContext();
{{~/if}}
private ApplicationEventPublisher applicationEventPublisher = new ApplicationEventPublisher() {
@Override
public void publishEvent(Object event) {
publishedEvents.add(event);
}
};
{{~#if (needsEventBus service)}}
private InMemoryEventPublisher eventPublisher = new InMemoryEventPublisher();
{{~/if}}
{{/if}}

{{~#each services as |service|}}
Expand All @@ -49,7 +48,7 @@ public class ServicesInMemoryConfig extends RepositoriesInMemoryConfig {
{{~/joinWithTemplate~}}
{{#if (includeEmitEventsImplementation service)}}
{{~#if (needsEventsProducer service)}}, eventsProducerInMemoryContext.{{eventsProducerInstance service.name}}(){{/if~}}
, applicationEventPublisher
{{~#if (needsEventBus service)}}, eventPublisher{{/if~}}
{{/if}}
);
{{~/each}}
Expand All @@ -72,6 +71,9 @@ public class ServicesInMemoryConfig extends RepositoriesInMemoryConfig {
{{entity.instanceName}}Repository().deleteAll();
{{entity.instanceName}}Repository().saveAll({{entity.instanceNamePlural}});
{{~/each}}
{{~#if (needsEventBus service)}}
eventPublisher.getEvents().clear();
{{~/if}}
}

{{~#if (includeEmitEventsImplementation service)}}
Expand All @@ -80,11 +82,11 @@ public class ServicesInMemoryConfig extends RepositoriesInMemoryConfig {
return eventsProducerInMemoryContext;
}
{{~/if}}

private List<Object> publishedEvents = new ArrayList<>();

public List<Object> getPublishedEvents() {
return publishedEvents;
{{~#if (needsEventBus service)}}
@Bean
public InMemoryEventPublisher eventPublisher() {
return eventPublisher;
}
{{~/if}}
{{~/if}}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package {{infrastructureEventsPackage}};

import {{domainEventsPackage}}.*;
import {{entitiesPackage}}.*;
import {{outboundEventsPackage}}.EventPublisher;

import java.util.ArrayList;
import java.util.List;

public class InMemoryEventPublisher implements EventPublisher {

private final List<Object> events = new ArrayList<>();

public List<Object> getEvents() {
return events;
}
{{~#each events as |event|}}
public void on{{event.className}}({{event.className}} event) {
events.add(event);
}
{{~/each}}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public void test_generator_hexagonal_jpa() throws Exception {
.withOption("persistence", PersistenceType.jpa)
.withOption("style", ProgrammingStyle.imperative)
.withOption("projectName", "customer-address")
.withOption("includeEmitEventsImplementation", false)
.withOption("forceOverwrite", true)
.withOption("haltOnFailFormatting", false);

Expand All @@ -63,6 +64,7 @@ public void test_generator_maps_id() throws Exception {
.withOption("persistence", PersistenceType.jpa)
.withOption("style", ProgrammingStyle.imperative)
.withOption("projectName", "customer-address")
.withOption("includeEmitEventsImplementation", false)
.withOption("forceOverwrite", true)
.withOption("haltOnFailFormatting", false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public void test_generator_simple_packaging_mongodb_customer_address() throws Ex
.withOption("style", ProgrammingStyle.imperative)
.withOption("projectName", "customer-address")
.withOption("simpleDomainPackaging", true)
.withOption("includeEmitEventsImplementation", false)
.withOption("forceOverwrite", true)
.withOption("haltOnFailFormatting", false);

Expand Down Expand Up @@ -110,7 +111,7 @@ public void test_generator_hexagonal_mongodb_orders_with_aggregate() throws Exce
.withOption("persistence", PersistenceType.mongodb)
.withOption("style", ProgrammingStyle.imperative)
.withOption("forceOverwrite", true)
// .withOption("includeEmitEventsImplementation", true)
.withOption("includeEmitEventsImplementation", false)
.withOption("haltOnFailFormatting", false);

new MainGenerator().generate(plugin);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public class ZDLToOpenAPIGenerator implements Generator {
@DocumentedOption(description = "JsonSchema type format for id fields and parameters.")
public String idTypeFormat = null;

@DocumentedOption(description = "Base OpenAPI file to merge with generated OpenAPI file")
public String baseOpenAPIFile = null;

@DocumentedOption(description = "Operation IDs to include. If empty, all operations will be included. (Supports Ant-style wildcards)")
public List<String> operationIdsToInclude;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,29 @@ public List<TemplateOutput> generate(Map<String, Object> contextModel) {
}
}

// include all internal root events (skip @asyncapi events)
var eventNames = new HashSet(JSONPath.get(apiModel, "$.services[*].methods[*].withEvents[*]", List.of()));
var flatEventNames = new ArrayList<String>();
for (var eventName : eventNames) {
if (eventName instanceof List eventNamesList) {
flatEventNames.addAll(eventNamesList);
} else {
flatEventNames.add((String) eventName);
}
}
var allEvents = new ArrayList<Map<String, Object>>();
for (Object eventName : flatEventNames) {
var event = JSONPath.get(apiModel, "$.events." + eventName);
if(event != null && JSONPath.get(event, "$.options.asyncapi") == null) {
allEvents.add((Map<String, Object>) event);
}
}
if(!allEvents.isEmpty()) {
for (TemplateInput template : templates.allEventsTemplates) {
templateOutputList.addAll(generateTemplateOutput(contextModel, template, Map.of("events", allEvents)));
}
}

Map<String, Map<String, Object>> services = JSONPath.get(apiModel, "$.options.options.service", Collections.emptyMap());
List<Map<String, Object>> servicesList = new ArrayList<>();
for (Map<String, Object> service : services.values()) {
Expand Down
Loading

0 comments on commit 10c4626

Please sign in to comment.