From c647d352d75a77e60778a8f00950e8631d6ca17b Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Sun, 22 Jan 2023 12:49:18 +0100 Subject: [PATCH 1/5] Added Architecture Layer Rules 1-12. --- .../sample/archunit/ArchitectureTest.java | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java b/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java index b9c6742..725d9fa 100644 --- a/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java @@ -21,10 +21,40 @@ public class ArchitectureTest { .layer("dataaccess").definedBy("..dataaccess..") // .layer("service").definedBy("..service..") // .layer("batch").definedBy("..batch..") // - .layer("ui").definedBy("..ui..") // + .layer("ui").definedBy("..ui..") + .layer("java").definedBy("..java..") // + .layer("javax").definedBy("..javax..") + .layer("mapper").definedBy("..mapstruct..") + .layer("logger").definedBy("..org.slf4j..") + .layer("database").definedBy("..com.querydsl..") + .layer("spring").definedBy("..org.springframework..") + .layer("client").definedBy("..client") + // TODO + //.whereLayer("ui").mayNotBeAccessedByAnyLayer() + .whereLayer("common").mayOnlyAccessLayers("java", "javax") + // L01: Common Layer doesnt depend on any other layer (except obv. java classes) + .whereLayer("client").mayOnlyBeAccessedByLayers("client", "java") + // L02: verifying that only client layer code may depend on client layer. + .whereLayer("client").mayOnlyAccessLayers("client", "java") + // L03: verifying that client layer does not depend on logic layer. + // L04: verifying that client layer does not depend on dataaccess layer. + // L05: verifying that client layer does not depend on batch layer. + .whereLayer("service").mayOnlyBeAccessedByLayers("ui", "client") + .whereLayer("service").mayOnlyAccessLayers("logic", "java", "javax", "common") + // L08: verifying that code from service layer does not depend on dataaccess layer. + // L06: verifying that service layer does not on depend batch layer. + .whereLayer("batch").mayOnlyAccessLayers("java") + // L07: verifying that batch layer does not depend on service layer. + // L11: verifying that batch layer does not depend on dataaccess layer. + .whereLayer("logic").mayOnlyBeAccessedByLayers("service") + .whereLayer("logic").mayOnlyAccessLayers("dataaccess", "java", "javax", "mapper", "common", "logger") + // L09: verifying that code from logic layer does not depend on service layer (of same app). + .whereLayer("dataaccess").mayOnlyBeAccessedByLayers("logic") + .whereLayer("dataaccess").mayOnlyAccessLayers("dataaccess", "java", "javax", "common", "spring", "database") + // L10: verifying that dataaccess layer does not depend on service layer. + // L12: verifying that dataaccess layer does not depend on logic layer. .because("Dependency of technical layers violates architecture rules."); - // ... } From 7d21655b6c696ba9d5221a8e22f1ab4e2c1c2899 Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Tue, 24 Jan 2023 17:44:31 +0100 Subject: [PATCH 2/5] refactoring + detailed LayerDependencyRulesTest --- .../sample/archunit/ArchitectureTest.java | 43 ++++---- .../archunit/LayerDependencyRulesTest.java | 97 +++++++++++++++++++ 2 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 src/test/java/com/devonfw/sample/archunit/LayerDependencyRulesTest.java diff --git a/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java b/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java index 725d9fa..f995716 100644 --- a/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java @@ -16,44 +16,35 @@ public class ArchitectureTest { @ArchTest private static final ArchRule shouldOnlyAccessValidLayers = // layeredArchitecture().consideringAllDependencies() // - .layer("common").definedBy("..common..") // - .layer("logic").definedBy("..logic..") // - .layer("dataaccess").definedBy("..dataaccess..") // - .layer("service").definedBy("..service..") // - .layer("batch").definedBy("..batch..") // - .layer("ui").definedBy("..ui..") - .layer("java").definedBy("..java..") // - .layer("javax").definedBy("..javax..") - .layer("mapper").definedBy("..mapstruct..") - .layer("logger").definedBy("..org.slf4j..") - .layer("database").definedBy("..com.querydsl..") - .layer("spring").definedBy("..org.springframework..") - .layer("client").definedBy("..client") - - // TODO - //.whereLayer("ui").mayNotBeAccessedByAnyLayer() - .whereLayer("common").mayOnlyAccessLayers("java", "javax") - // L01: Common Layer doesnt depend on any other layer (except obv. java classes) - .whereLayer("client").mayOnlyBeAccessedByLayers("client", "java") + .layer("common").definedBy("com.devonfw.sample.archunit.common..") // + .layer("logic").definedBy("com.devonfw.sample.archunit.logic..") // + .layer("dataaccess").definedBy("com.devonfw.sample.archunit.dataaccess..") // + .layer("service").definedBy("com.devonfw.sample.archunit.service..") // + .layer("client").definedBy("com.devonfw.sample.archunit.client..") + + .whereLayer("common").mayOnlyAccessLayers("common") + // L01: Common Layer doesnt depend on any other layer + .whereLayer("client").mayOnlyBeAccessedByLayers("client") // L02: verifying that only client layer code may depend on client layer. - .whereLayer("client").mayOnlyAccessLayers("client", "java") + .whereLayer("client").mayOnlyAccessLayers("client") // L03: verifying that client layer does not depend on logic layer. // L04: verifying that client layer does not depend on dataaccess layer. // L05: verifying that client layer does not depend on batch layer. - .whereLayer("service").mayOnlyBeAccessedByLayers("ui", "client") - .whereLayer("service").mayOnlyAccessLayers("logic", "java", "javax", "common") + .whereLayer("service").mayOnlyBeAccessedByLayers("client") + .whereLayer("service").mayOnlyAccessLayers("logic", "common") // L08: verifying that code from service layer does not depend on dataaccess layer. // L06: verifying that service layer does not on depend batch layer. - .whereLayer("batch").mayOnlyAccessLayers("java") + //.whereLayer("batch").mayOnlyAccessLayers("java") // L07: verifying that batch layer does not depend on service layer. // L11: verifying that batch layer does not depend on dataaccess layer. - .whereLayer("logic").mayOnlyBeAccessedByLayers("service") - .whereLayer("logic").mayOnlyAccessLayers("dataaccess", "java", "javax", "mapper", "common", "logger") + .whereLayer("logic").mayOnlyBeAccessedByLayers("service") + .whereLayer("logic").mayOnlyAccessLayers("dataaccess", "common") // L09: verifying that code from logic layer does not depend on service layer (of same app). .whereLayer("dataaccess").mayOnlyBeAccessedByLayers("logic") - .whereLayer("dataaccess").mayOnlyAccessLayers("dataaccess", "java", "javax", "common", "spring", "database") + .whereLayer("dataaccess").mayOnlyAccessLayers("dataaccess", "common") // L10: verifying that dataaccess layer does not depend on service layer. // L12: verifying that dataaccess layer does not depend on logic layer. + .withOptionalLayers(true) .because("Dependency of technical layers violates architecture rules."); // ... diff --git a/src/test/java/com/devonfw/sample/archunit/LayerDependencyRulesTest.java b/src/test/java/com/devonfw/sample/archunit/LayerDependencyRulesTest.java new file mode 100644 index 0000000..f3e5fe2 --- /dev/null +++ b/src/test/java/com/devonfw/sample/archunit/LayerDependencyRulesTest.java @@ -0,0 +1,97 @@ +package com.devonfw.sample.archunit; + +import com.tngtech.archunit.core.importer.ImportOption; +import com.tngtech.archunit.junit.AnalyzeClasses; +import com.tngtech.archunit.junit.ArchTest; +import com.tngtech.archunit.lang.ArchRule; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + +@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) +public class LayerDependencyRulesTest { + + // 'access' catches only violations by real accesses, i.e. accessing a field, calling a method; compare 'dependOn' further down + + // L09: verifying that code from logic layer does not access service layer (of same app). + @ArchTest + static final ArchRule logic_should_not_access_services = + noClasses().that().resideInAPackage("..logic..") + .should().accessClassesThat().resideInAPackage("..service.."); + + // L10: verifying that dataaccess layer does not access service layer. + // L12: verifying that dataaccess layer does not access logic layer. + @ArchTest + static final ArchRule persistence_should_not_access_services_or_logic = + noClasses().that().resideInAPackage("..dataaccess..") + .should().accessClassesThat().resideInAnyPackage("..service..", "..logic.."); + + @ArchTest + static final ArchRule services_should_only_be_accessed_by_clients_or_other_services = + classes().that().resideInAPackage("..service..") + .should().onlyBeAccessed().byAnyPackage("..client..", "..service.."); + + @ArchTest + static final ArchRule services_should_only_access_logic_common_or_other_services = + classes().that().resideInAPackage("..service..") + .should().onlyAccessClassesThat().resideInAnyPackage("..service..", "..common..", "..logic..", "java..", "javax.."); + + // 'dependOn' catches a wider variety of violations, e.g. having fields of type, having method parameters of type, extending type ... + + // L09: verifying that code from logic layer does not depend on service layer (of same app). + @ArchTest + static final ArchRule logic_should_not_depend_on_services = + noClasses().that().resideInAPackage("..logic..") + .should().dependOnClassesThat().resideInAPackage("..service.."); + + // L06: verifying that service layer does not depend on batch layer. + // L08: verifying that service layer does not depend on dataaccess layer. + @ArchTest + static final ArchRule services_should_not_depend_on_batch_or_persistence = + noClasses().that().resideInAPackage("..service..") + .should().dependOnClassesThat().resideInAnyPackage("..batch..", "..dataaccess.."); + + // L10: verifying that dataaccess layer does not depend on service layer. + // L12: verifying that dataaccess layer does not depend on logic layer. + @ArchTest + static final ArchRule persistence_should_not_depend_on_services_or_logic = + noClasses().that().resideInAPackage("..dataaccess..") + .should().dependOnClassesThat().resideInAnyPackage("..service..", "..logic.."); + + @ArchTest + static final ArchRule services_should_only_be_depended_on_by_controllers_or_other_services = + classes().that().resideInAPackage("..service..") + .should().onlyHaveDependentClassesThat().resideInAnyPackage("..controller..", "..service.."); + + @ArchTest + static final ArchRule services_should_only_depend_on_logic_common_or_other_services = + classes().that().resideInAPackage("..service..") + .should().onlyDependOnClassesThat().resideInAnyPackage("..service..", "..logic..", "..common..", "java..", "javax.."); + + // L01: Common Layer does not depend on any other layer + @ArchTest + static final ArchRule common_should_only_depend_on_common = + classes().that().resideInAPackage("..common..") + .should().onlyDependOnClassesThat().resideInAnyPackage("..common..", "java..", "javax.."); + + // L02: verifying that only client layer code may depend on client layer. + @ArchTest + static final ArchRule client_should_only_depend_on_client = + classes().that().resideInAPackage("..client..") + .should().onlyDependOnClassesThat().resideInAnyPackage("..client..", "java..", "javax..").allowEmptyShould(true); + + // L03: verifying that client layer does not depend on logic layer. + // L04: verifying that client layer does not depend on dataaccess layer. + // L05: verifying that client layer does not depend on batch layer. + @ArchTest + static final ArchRule client_should_not_depend_on_logic_persistence_or_batch_layer = + noClasses().that().resideInAPackage("..client..") + .should().dependOnClassesThat().resideInAnyPackage("..logic..", "..dataaccess..", "..batch..").allowEmptyShould(true); + + // L07: verifying that batch layer does not depend on service layer. + // L11: verifying that batch layer does not depend on dataaccess layer. + @ArchTest + static final ArchRule batch_should_not_depend_on_service_or_persistence = + noClasses().that().resideInAPackage("..batch..") + .should().dependOnClassesThat().resideInAnyPackage("..service..", "..dataaccess..").allowEmptyShould(true); +} From 0c97565f8ffc2bda33d771e24818b01f06366100 Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Mon, 30 Jan 2023 11:38:27 +0100 Subject: [PATCH 3/5] delete redundant layer rules check. LayerDependencyRulesTest.java includes all necessary tests. --- .../sample/archunit/ArchitectureTest.java | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java b/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java index f995716..50f5cd2 100644 --- a/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java @@ -22,28 +22,6 @@ public class ArchitectureTest { .layer("service").definedBy("com.devonfw.sample.archunit.service..") // .layer("client").definedBy("com.devonfw.sample.archunit.client..") - .whereLayer("common").mayOnlyAccessLayers("common") - // L01: Common Layer doesnt depend on any other layer - .whereLayer("client").mayOnlyBeAccessedByLayers("client") - // L02: verifying that only client layer code may depend on client layer. - .whereLayer("client").mayOnlyAccessLayers("client") - // L03: verifying that client layer does not depend on logic layer. - // L04: verifying that client layer does not depend on dataaccess layer. - // L05: verifying that client layer does not depend on batch layer. - .whereLayer("service").mayOnlyBeAccessedByLayers("client") - .whereLayer("service").mayOnlyAccessLayers("logic", "common") - // L08: verifying that code from service layer does not depend on dataaccess layer. - // L06: verifying that service layer does not on depend batch layer. - //.whereLayer("batch").mayOnlyAccessLayers("java") - // L07: verifying that batch layer does not depend on service layer. - // L11: verifying that batch layer does not depend on dataaccess layer. - .whereLayer("logic").mayOnlyBeAccessedByLayers("service") - .whereLayer("logic").mayOnlyAccessLayers("dataaccess", "common") - // L09: verifying that code from logic layer does not depend on service layer (of same app). - .whereLayer("dataaccess").mayOnlyBeAccessedByLayers("logic") - .whereLayer("dataaccess").mayOnlyAccessLayers("dataaccess", "common") - // L10: verifying that dataaccess layer does not depend on service layer. - // L12: verifying that dataaccess layer does not depend on logic layer. .withOptionalLayers(true) .because("Dependency of technical layers violates architecture rules."); // ... From 26f82b75655fa5f7eaea58f8699c9461521db21b Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Tue, 31 Jan 2023 10:47:25 +0100 Subject: [PATCH 4/5] Refactoring: deleted comments for better readability --- .../sample/archunit/ArchitectureTest.java | 22 +++-- .../archunit/LayerDependencyRulesTest.java | 97 ------------------- 2 files changed, 15 insertions(+), 104 deletions(-) delete mode 100644 src/test/java/com/devonfw/sample/archunit/LayerDependencyRulesTest.java diff --git a/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java b/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java index 50f5cd2..0cabe4e 100644 --- a/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java @@ -15,14 +15,22 @@ public class ArchitectureTest { @ArchTest private static final ArchRule shouldOnlyAccessValidLayers = // - layeredArchitecture().consideringAllDependencies() // - .layer("common").definedBy("com.devonfw.sample.archunit.common..") // - .layer("logic").definedBy("com.devonfw.sample.archunit.logic..") // - .layer("dataaccess").definedBy("com.devonfw.sample.archunit.dataaccess..") // - .layer("service").definedBy("com.devonfw.sample.archunit.service..") // - .layer("client").definedBy("com.devonfw.sample.archunit.client..") + layeredArchitecture().consideringAllDependencies() // + .layer("common").definedBy("..common..") // + .layer("logic").definedBy("..logic..") // + .layer("dataaccess").definedBy("..dataaccess..") // + .layer("service").definedBy("..service..") // + .layer("client").definedBy("..client..") + .layer("batch").definedBy("..batch..") - .withOptionalLayers(true) + .whereLayer("client").mayNotBeAccessedByAnyLayer() + .whereLayer("batch").mayOnlyBeAccessedByLayers( "logic") + .whereLayer("service").mayOnlyBeAccessedByLayers("client") + .whereLayer("logic").mayOnlyBeAccessedByLayers("service", "batch") + .whereLayer("dataaccess").mayOnlyBeAccessedByLayers("logic") + .whereLayer("common").mayOnlyBeAccessedByLayers("common", "dataaccess", "logic", "service") + + .withOptionalLayers(true) .because("Dependency of technical layers violates architecture rules."); // ... diff --git a/src/test/java/com/devonfw/sample/archunit/LayerDependencyRulesTest.java b/src/test/java/com/devonfw/sample/archunit/LayerDependencyRulesTest.java deleted file mode 100644 index f3e5fe2..0000000 --- a/src/test/java/com/devonfw/sample/archunit/LayerDependencyRulesTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.devonfw.sample.archunit; - -import com.tngtech.archunit.core.importer.ImportOption; -import com.tngtech.archunit.junit.AnalyzeClasses; -import com.tngtech.archunit.junit.ArchTest; -import com.tngtech.archunit.lang.ArchRule; - -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - -@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) -public class LayerDependencyRulesTest { - - // 'access' catches only violations by real accesses, i.e. accessing a field, calling a method; compare 'dependOn' further down - - // L09: verifying that code from logic layer does not access service layer (of same app). - @ArchTest - static final ArchRule logic_should_not_access_services = - noClasses().that().resideInAPackage("..logic..") - .should().accessClassesThat().resideInAPackage("..service.."); - - // L10: verifying that dataaccess layer does not access service layer. - // L12: verifying that dataaccess layer does not access logic layer. - @ArchTest - static final ArchRule persistence_should_not_access_services_or_logic = - noClasses().that().resideInAPackage("..dataaccess..") - .should().accessClassesThat().resideInAnyPackage("..service..", "..logic.."); - - @ArchTest - static final ArchRule services_should_only_be_accessed_by_clients_or_other_services = - classes().that().resideInAPackage("..service..") - .should().onlyBeAccessed().byAnyPackage("..client..", "..service.."); - - @ArchTest - static final ArchRule services_should_only_access_logic_common_or_other_services = - classes().that().resideInAPackage("..service..") - .should().onlyAccessClassesThat().resideInAnyPackage("..service..", "..common..", "..logic..", "java..", "javax.."); - - // 'dependOn' catches a wider variety of violations, e.g. having fields of type, having method parameters of type, extending type ... - - // L09: verifying that code from logic layer does not depend on service layer (of same app). - @ArchTest - static final ArchRule logic_should_not_depend_on_services = - noClasses().that().resideInAPackage("..logic..") - .should().dependOnClassesThat().resideInAPackage("..service.."); - - // L06: verifying that service layer does not depend on batch layer. - // L08: verifying that service layer does not depend on dataaccess layer. - @ArchTest - static final ArchRule services_should_not_depend_on_batch_or_persistence = - noClasses().that().resideInAPackage("..service..") - .should().dependOnClassesThat().resideInAnyPackage("..batch..", "..dataaccess.."); - - // L10: verifying that dataaccess layer does not depend on service layer. - // L12: verifying that dataaccess layer does not depend on logic layer. - @ArchTest - static final ArchRule persistence_should_not_depend_on_services_or_logic = - noClasses().that().resideInAPackage("..dataaccess..") - .should().dependOnClassesThat().resideInAnyPackage("..service..", "..logic.."); - - @ArchTest - static final ArchRule services_should_only_be_depended_on_by_controllers_or_other_services = - classes().that().resideInAPackage("..service..") - .should().onlyHaveDependentClassesThat().resideInAnyPackage("..controller..", "..service.."); - - @ArchTest - static final ArchRule services_should_only_depend_on_logic_common_or_other_services = - classes().that().resideInAPackage("..service..") - .should().onlyDependOnClassesThat().resideInAnyPackage("..service..", "..logic..", "..common..", "java..", "javax.."); - - // L01: Common Layer does not depend on any other layer - @ArchTest - static final ArchRule common_should_only_depend_on_common = - classes().that().resideInAPackage("..common..") - .should().onlyDependOnClassesThat().resideInAnyPackage("..common..", "java..", "javax.."); - - // L02: verifying that only client layer code may depend on client layer. - @ArchTest - static final ArchRule client_should_only_depend_on_client = - classes().that().resideInAPackage("..client..") - .should().onlyDependOnClassesThat().resideInAnyPackage("..client..", "java..", "javax..").allowEmptyShould(true); - - // L03: verifying that client layer does not depend on logic layer. - // L04: verifying that client layer does not depend on dataaccess layer. - // L05: verifying that client layer does not depend on batch layer. - @ArchTest - static final ArchRule client_should_not_depend_on_logic_persistence_or_batch_layer = - noClasses().that().resideInAPackage("..client..") - .should().dependOnClassesThat().resideInAnyPackage("..logic..", "..dataaccess..", "..batch..").allowEmptyShould(true); - - // L07: verifying that batch layer does not depend on service layer. - // L11: verifying that batch layer does not depend on dataaccess layer. - @ArchTest - static final ArchRule batch_should_not_depend_on_service_or_persistence = - noClasses().that().resideInAPackage("..batch..") - .should().dependOnClassesThat().resideInAnyPackage("..service..", "..dataaccess..").allowEmptyShould(true); -} From 6f75cf694b5aff8a690bafdc9dd03a1723a2b332 Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Mon, 6 Feb 2023 10:26:56 +0100 Subject: [PATCH 5/5] Deleted the common layer rule. Postponed its implementation as discussed. --- .../java/com/devonfw/sample/archunit/ArchitectureTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java b/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java index 0cabe4e..b8a6ca2 100644 --- a/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ArchitectureTest.java @@ -24,12 +24,10 @@ public class ArchitectureTest { .layer("batch").definedBy("..batch..") .whereLayer("client").mayNotBeAccessedByAnyLayer() - .whereLayer("batch").mayOnlyBeAccessedByLayers( "logic") + .whereLayer("batch").mayNotBeAccessedByAnyLayer() .whereLayer("service").mayOnlyBeAccessedByLayers("client") .whereLayer("logic").mayOnlyBeAccessedByLayers("service", "batch") .whereLayer("dataaccess").mayOnlyBeAccessedByLayers("logic") - .whereLayer("common").mayOnlyBeAccessedByLayers("common", "dataaccess", "logic", "service") - .withOptionalLayers(true) .because("Dependency of technical layers violates architecture rules."); // ...