diff --git a/src/main/resources/generator/server/javatool/arch/HexagonalArchTest.java.mustache b/src/main/resources/generator/server/javatool/arch/HexagonalArchTest.java.mustache index a623901fcfe..4dc08a8176a 100644 --- a/src/main/resources/generator/server/javatool/arch/HexagonalArchTest.java.mustache +++ b/src/main/resources/generator/server/javatool/arch/HexagonalArchTest.java.mustache @@ -1,6 +1,7 @@ package {{packageName}}; -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.ClassFileImporter; @@ -15,7 +16,6 @@ import java.util.Collection; import java.util.List; import java.util.function.Function; import java.util.stream.Stream; - import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.stereotype.Component; @@ -31,23 +31,19 @@ class HexagonalArchTest { .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) .importPackages(ROOT_PACKAGE); - private static final Collection businessContexts = buildBusinessContexts(); - private static final Collection sharedKernels = buildSharedKernels(); - - // the empty package is related to: https://github.com/TNG/ArchUnit/issues/191#issuecomment-507964792 - private static final Collection vanillaPackages = Arrays.asList("java..", ""); - private static final Collection commonToolsAndUtilsPackages = Arrays.asList("org.slf4j..", "org.apache.commons..", "com.google.guava.."); - + private static final Collection businessContexts = getPackageAnnotatedBy(BusinessContext.class); private static final Collection businessContextsPackages = toPackages(businessContexts); - private static final Collection sharedKernelsPackages = toPackages(sharedKernels); - private static Collection buildBusinessContexts() { - return getPackageAnnotatedBy(BusinessContext.class); - } + private static final Collection sharedKernels = getPackageAnnotatedBy(SharedKernel.class); + private static final Collection sharedKernelsPackages = toPackages(sharedKernels); - private static Collection buildSharedKernels() { - return getPackageAnnotatedBy(SharedKernel.class); - } + // the empty package is related to: https://github.com/TNG/ArchUnit/issues/191#issuecomment-507964792 + private static final Collection vanillaPackages = Arrays.asList("java..", ""); + private static final Collection commonToolsAndUtilsPackages = Arrays.asList( + "org.slf4j..", + "org.apache.commons..", + "com.google.guava.." + ); private static Collection toPackages(Collection packages) { return packages.stream().map(path -> path + "..").toList(); @@ -121,6 +117,7 @@ class HexagonalArchTest { .onlyBeAccessed() .byClassesThat() .resideInAPackage("..secondary..") + .because("To interact between two contexts, secondary from context A should call a primary Java adapter (naming convention starting with 'Java') from context B") .check(classes); } @@ -137,11 +134,11 @@ class HexagonalArchTest { sharedKernelsPackages.forEach(kernel -> noClasses() .that() - .resideInAnyPackage(kernel) + .resideInAPackage(kernel) .should() .dependOnClassesThat() .resideInAnyPackage(businessContextsPackages.toArray(String[]::new)) - .because("Shared kernels should not have dependencies to bounded contexts") + .because("Shared kernels should not have dependencies to bounded contexts, only the opposite is allowed") .check(classes) ); } @@ -154,7 +151,7 @@ class HexagonalArchTest { void shouldNotDependOnOutside() { classes() .that() - .resideInAnyPackage(".domain..") + .resideInAPackage(".domain..") .should() .onlyDependOnClassesThat() .resideInAnyPackage(authorizedContextPackages(".domain..")) @@ -163,7 +160,10 @@ class HexagonalArchTest { } private String[] authorizedContextPackages(String packageName) { - return Stream.of(List.of(packageName), vanillaPackages, commonToolsAndUtilsPackages, sharedKernelsPackages).flatMap(Collection::stream).toArray(String[]::new); + return Stream + .of(List.of(packageName), vanillaPackages, commonToolsAndUtilsPackages, sharedKernelsPackages) + .flatMap(Collection::stream) + .toArray(String[]::new); } } @@ -174,10 +174,10 @@ class HexagonalArchTest { void shouldNotDependOnInfrastructure() { noClasses() .that() - .resideInAnyPackage("..application..") + .resideInAPackage("..application..") .should() .dependOnClassesThat() - .resideInAnyPackage("..infrastructure..") + .resideInAPackage("..infrastructure..") .because("Application should only depend on domain, not on infrastructure") .check(classes); } @@ -190,10 +190,10 @@ class HexagonalArchTest { void shouldNotDependOnSecondary() { noClasses() .that() - .resideInAnyPackage("..primary..") + .resideInAPackage("..primary..") .should() .dependOnClassesThat() - .resideInAnyPackage("..secondary..") + .resideInAPackage("..secondary..") .because("Primary should not interact with secondary") .check(classes); } @@ -211,12 +211,26 @@ class HexagonalArchTest { void shouldNotDependOnApplication() { noClasses() .that() - .resideInAnyPackage("..infrastructure.secondary..") + .resideInAPackage("..infrastructure.secondary..") .should() .dependOnClassesThat() - .resideInAnyPackage("..application..") + .resideInAPackage("..application..") .because("Secondary should not depend on application") .check(classes); } + + @Test + void shouldNotDependOnSameContextPrimary() { + Stream.concat(businessContexts.stream(), sharedKernels.stream()).forEach(context -> { + noClasses() + .that() + .resideInAPackage(context + ".infrastructure.secondary..") + .should() + .dependOnClassesThat() + .resideInAPackage(context + ".infrastructure.primary") + .because("Secondary should not loop to its own context's primary") + .check(classes); + }); + } } } diff --git a/src/test/java/tech/jhipster/lite/HexagonalArchTest.java b/src/test/java/tech/jhipster/lite/HexagonalArchTest.java index 91e08245768..c0b9d118579 100644 --- a/src/test/java/tech/jhipster/lite/HexagonalArchTest.java +++ b/src/test/java/tech/jhipster/lite/HexagonalArchTest.java @@ -31,8 +31,11 @@ class HexagonalArchTest { .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) .importPackages(ROOT_PACKAGE); - private static final Collection businessContexts = buildBusinessContexts(); - private static final Collection sharedKernels = buildSharedKernels(); + private static final Collection businessContexts = getPackageAnnotatedBy(BusinessContext.class); + private static final Collection businessContextsPackages = toPackages(businessContexts); + + private static final Collection sharedKernels = getPackageAnnotatedBy(SharedKernel.class); + private static final Collection sharedKernelsPackages = toPackages(sharedKernels); // the empty package is related to: https://github.com/TNG/ArchUnit/issues/191#issuecomment-507964792 private static final Collection vanillaPackages = Arrays.asList("java..", ""); @@ -42,17 +45,6 @@ class HexagonalArchTest { "com.google.guava.." ); - private static final Collection businessContextsPackages = toPackages(businessContexts); - private static final Collection sharedKernelsPackages = toPackages(sharedKernels); - - private static Collection buildBusinessContexts() { - return getPackageAnnotatedBy(BusinessContext.class); - } - - private static Collection buildSharedKernels() { - return getPackageAnnotatedBy(SharedKernel.class); - } - private static Collection toPackages(Collection packages) { return packages.stream().map(path -> path + "..").toList(); } @@ -125,6 +117,9 @@ void primaryJavaAdaptersShouldOnlyBeCalledFromSecondaries() { .onlyBeAccessed() .byClassesThat() .resideInAPackage("..secondary..") + .because( + "To interact between two contexts, secondary from context A should call a primary Java adapter (naming convention starting with 'Java') from context B" + ) .check(classes); } @@ -141,11 +136,11 @@ void shouldNotDependOnBoundedContexts() { sharedKernelsPackages.forEach(kernel -> noClasses() .that() - .resideInAnyPackage(kernel) + .resideInAPackage(kernel) .should() .dependOnClassesThat() .resideInAnyPackage(businessContextsPackages.toArray(String[]::new)) - .because("Shared kernels should not have dependencies to bounded contexts") + .because("Shared kernels should not have dependencies to bounded contexts, only the opposite is allowed") .check(classes) ); } @@ -158,7 +153,7 @@ class Domain { void shouldNotDependOnOutside() { classes() .that() - .resideInAnyPackage(".domain..") + .resideInAPackage(".domain..") .should() .onlyDependOnClassesThat() .resideInAnyPackage(authorizedContextPackages(".domain..")) @@ -181,10 +176,10 @@ class Application { void shouldNotDependOnInfrastructure() { noClasses() .that() - .resideInAnyPackage("..application..") + .resideInAPackage("..application..") .should() .dependOnClassesThat() - .resideInAnyPackage("..infrastructure..") + .resideInAPackage("..infrastructure..") .because("Application should only depend on domain, not on infrastructure") .check(classes); } @@ -197,10 +192,10 @@ class Primary { void shouldNotDependOnSecondary() { noClasses() .that() - .resideInAnyPackage("..primary..") + .resideInAPackage("..primary..") .should() .dependOnClassesThat() - .resideInAnyPackage("..secondary..") + .resideInAPackage("..secondary..") .because("Primary should not interact with secondary") .check(classes); } @@ -218,12 +213,28 @@ class Secondary { void shouldNotDependOnApplication() { noClasses() .that() - .resideInAnyPackage("..infrastructure.secondary..") + .resideInAPackage("..infrastructure.secondary..") .should() .dependOnClassesThat() - .resideInAnyPackage("..application..") + .resideInAPackage("..application..") .because("Secondary should not depend on application") .check(classes); } + + @Test + void shouldNotDependOnSameContextPrimary() { + Stream + .concat(businessContexts.stream(), sharedKernels.stream()) + .forEach(context -> { + noClasses() + .that() + .resideInAPackage(context + ".infrastructure.secondary..") + .should() + .dependOnClassesThat() + .resideInAPackage(context + ".infrastructure.primary") + .because("Secondary should not loop to its own context's primary") + .check(classes); + }); + } } }