You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/07/20 09:34:29 UTC
[isis] 01/02: ISIS-2807: sketches out arch tests for entities,
domain objects and entities
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch ISIS-2807-arch-tests
in repository https://gitbox.apache.org/repos/asf/isis.git
commit ab1947c06243277607ad9fe8485f85c8febb866f
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Tue Jul 20 10:26:53 2021 +0100
ISIS-2807: sketches out arch tests for entities, domain objects and entities
... and updates docs
---
.../adoc/modules/archtestsupport/pages/about.adoc | 85 +++------
testing/archtestsupport/applib/pom.xml | 8 +
.../applib/classrules/ArchitectureClassRules.java | 35 ----
.../classrules/ArchitectureDomainObjectRules.java | 57 ++++++
.../classrules/ArchitectureDomainServiceRules.java | 58 ++++++
.../applib/classrules/ArchitectureJdoRules.java | 77 ++++++++
.../applib/classrules/ArchitectureJpaRules.java | 95 ++++++++++
.../classrules/CommonDescribedPredicates.java | 45 +++++
.../modulerules/ArchitectureModuleRules.java | 199 ++++++++++++++++-----
.../applib/modulerules/Subpackage.java | 48 +++++
.../applib/modulerules/SubpackageEnum.java | 169 +++++++++++++++++
.../packagerules/ArchitecturePackageRules.java | 33 ----
.../applib/packagerules/Subpackage.java | 20 ---
.../applib/packagerules/SubpackageEnum.java | 69 -------
.../applib/packagerules/SubpackageType.java | 22 ---
15 files changed, 743 insertions(+), 277 deletions(-)
diff --git a/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc b/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
index 502bbeb..30b5da4 100644
--- a/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
+++ b/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
@@ -48,85 +48,54 @@ public class ModuleTests {
<.> just obtains the list of modules
-== Package Tests
+== Module Package Tests
CAUTION: TODO - to flesh out
+Defines the rules for which subpackages or a module to check and whether the classes in those subpackages can access the classes in other subpackages either for the same module, or for modules that are referenced (`Import`ed) directly or transitively.
+
+
[source,java]
.Subpackage
----
public interface Subpackage {
- String getName();
-
- SubpackageType getSubpackageType();
-
- String packageIdentifierWithin(Class<?> moduleClass);
- default void defineLayer(Architectures.LayeredArchitecture layeredArchitecture, Class<?> moduleClass) {
- val layerDefinition = getSubpackageType().defineLayer(this, moduleClass, layeredArchitecture);
- layerDefinition.definedBy(packageIdentifierWithin(moduleClass));
- }
-
- boolean canReference(Subpackage referenced);
+ String getName(); // <.>
+ List<String> mayBeAccessedBySubpackagesInSameModule(); // <.>
+ List<String> mayBeAccessedBySubpackagesInReferencingModules(); // <.>
}
----
-The `SubpackageEnum` provides an off the shelf implementation.
+<.> The name of the subpackage, for example "dom", "api", "spi" or "fixtures".
-[source,java]
-.PackageTestsForCustomerModule.java
-----
-@AnalyzeClasses(
- packagesOf = {CustomerModule.class}, // <.>
- importOptions = {ImportOption.DoNotIncludeTests.class}
-)
-public class PackageTestsForCustomerModule {
+<.> A list of the (names of the) subpackages where classes in the same module as this package have access.
++
+For example, the "dom" subpackage can probably be referenced from the "menu" subpackage, but not vice versa.
++
+The special value of "*" is a wildcard meaning that all subpackages (in the same module) can access.
- @ArchTest public static ArchRule code_dependencies_follow_module_subpackages =
- ArchitecturePackageRules.code_dependencies_follow_module_subpackages( // <.>
- CustomerModule.class, // <1>
- Arrays.asList(SubpackageEnum.values())); // <.>
+<.> A list of the (names of the) subpackages where classes in the packages of other referencing modules may have access.
++
+For example, in some cases the the "dom" subpackage may <i>not</i> be accessible from other modules if the intention is to require all programmatic access through an "api" subpackage (where the classes in `dom` implement interfaces defined in `api`).
++
+The special value of "*" is a wildcard meaning that all subpackages (in other modules) can access.
-}
-----
-<.> the module to be analyzed.
-A similar test should be created for each and every module in the app.
-<.> xxx
-<.> xxx
+The `SubpackageEnum` provides an off the shelf implementation.
+
+== Entity Tests
-== Class Tests
+These are provided for both JPA and JDO.
CAUTION: TODO - to flesh out
-[source,java]
-.ClassTests.java
-----
-@AnalyzeClasses(
- packagesOf = {
- CustomerModule.class // <.>
- , OrderModule.class
- , ProductModule.class
- },
- importOptions = { ImportOption.DoNotIncludeTests.class }
-)
-public class ClassTests {
- @ArchTest
- static ArchRule classes_annotated_with_Entity_are_also_annotated_with_DomainObject =
- ArchitectureClassRules.classes_annotated_with_Entity_must_also_be_annotated_with_DomainObject(); // <.>
+== DomainObjectTests
- @ArchTest
- static ArchRule classes_annotated_with_Entity_are_also_annotated_with_XmlJavaTypeAdapter =
- ArchitectureClassRules.classes_annotated_with_Entity_must_also_be_annotated_with_XmlJavaAdapter(); // <.>
+CAUTION: TODO - to flesh out
- @ArchTest
- static ArchRule classes_annotated_with_DomainObject_are_also_annotated_with_DomainObjectLayout =
- ArchitectureClassRules.classes_annotated_with_DomainObject_must_also_be_annotated_with_DomainObjectLayout(); // <.>
-----
+== DomainServiceTests
+
+CAUTION: TODO - to flesh out
-<.> the modules of the application to be scanned
-<.> xxx
-<.> xxx
-<.> xxx
diff --git a/testing/archtestsupport/applib/pom.xml b/testing/archtestsupport/applib/pom.xml
index 1ce9cd1..b408913 100644
--- a/testing/archtestsupport/applib/pom.xml
+++ b/testing/archtestsupport/applib/pom.xml
@@ -75,6 +75,7 @@
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5-api</artifactId>
</dependency>
+
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5-engine</artifactId>
@@ -85,6 +86,13 @@
<artifactId>slf4j-api</artifactId>
</dependency>
+ <!-- PROVIDED (to avoid polluting the classpath -->
+ <dependency>
+ <groupId>org.datanucleus</groupId>
+ <artifactId>javax.jdo</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
</dependencies>
<profiles>
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureClassRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureClassRules.java
deleted file mode 100644
index eb8eba2..0000000
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureClassRules.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.apache.isis.testing.archtestsupport.applib.classrules;
-
-import javax.persistence.Entity;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-import com.tngtech.archunit.lang.ArchRule;
-
-import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
-
-import org.apache.isis.applib.annotation.DomainObject;
-import org.apache.isis.applib.annotation.DomainObjectLayout;
-
-import lombok.experimental.UtilityClass;
-
-@UtilityClass
-public class ArchitectureClassRules {
-
- public static ArchRule classes_annotated_with_Entity_must_also_be_annotated_with_DomainObject() {
- return classes()
- .that().areAnnotatedWith(Entity.class)
- .should().beAnnotatedWith(DomainObject.class);
- }
-
- public static ArchRule classes_annotated_with_Entity_must_also_be_annotated_with_XmlJavaAdapter() {
- return classes()
- .that().areAnnotatedWith(Entity.class)
- .should().beAnnotatedWith(XmlJavaTypeAdapter.class);
- }
-
- public static ArchRule classes_annotated_with_DomainObject_must_also_be_annotated_with_DomainObjectLayout() {
- return classes()
- .that().areAnnotatedWith(DomainObject.class)
- .should().beAnnotatedWith(DomainObjectLayout.class);
- }
-}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainObjectRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainObjectRules.java
new file mode 100644
index 0000000..cc61e78
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainObjectRules.java
@@ -0,0 +1,57 @@
+package org.apache.isis.testing.archtestsupport.applib.classrules;
+
+import com.tngtech.archunit.base.DescribedPredicate;
+import com.tngtech.archunit.core.domain.JavaAnnotation;
+import com.tngtech.archunit.lang.ArchRule;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+import lombok.experimental.UtilityClass;
+import lombok.val;
+
+/**
+ * A library of architecture tests to ensure coding conventions are followed for classes annotated with
+ * {@link DomainObject}.
+ *
+ * @since 2.0 {@index}
+ */
+@UtilityClass
+public class ArchitectureDomainObjectRules {
+
+ /**
+ * This rule requires that classes annotated with the {@link DomainObject} annotation must specify their
+ * {@link DomainObject#logicalTypeName() logicalTypeName}.
+ */
+ public static ArchRule classes_annotated_with_DomainObject_must_specify_logicalTypeName() {
+ return classes()
+ .that().areAnnotatedWith(DomainObject.class)
+ .should().beAnnotatedWith(DomainObject_logicalTypeName());
+ }
+
+ static DescribedPredicate<JavaAnnotation<?>> DomainObject_logicalTypeName() {
+ return new DescribedPredicate<JavaAnnotation<?>>("@DomainObject(logicalTypeName=...)") {
+ @Override public boolean apply(final JavaAnnotation<?> javaAnnotation) {
+ if (javaAnnotation.getRawType().isEquivalentTo(DomainObject.class)) {
+ return false;
+ }
+ val properties = javaAnnotation.getProperties();
+ val value = properties.get("logicalTypeName");
+ return value instanceof String && ((String) value).length() > 0;
+ }
+ };
+ }
+
+ /**
+ * This rule requires that classes annotated with the {@link DomainObject} annotation must also be
+ * annotated with the {@link DomainObjectLayout} annotation.
+ */
+ public static ArchRule classes_annotated_with_DomainObject_must_also_be_annotated_with_DomainObjectLayout() {
+ return classes()
+ .that().areAnnotatedWith(DomainObject.class)
+ .should().beAnnotatedWith(DomainObjectLayout.class);
+ }
+
+
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainServiceRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainServiceRules.java
new file mode 100644
index 0000000..5ab80f9
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainServiceRules.java
@@ -0,0 +1,58 @@
+package org.apache.isis.testing.archtestsupport.applib.classrules;
+
+import com.tngtech.archunit.base.DescribedPredicate;
+import com.tngtech.archunit.core.domain.JavaAnnotation;
+import com.tngtech.archunit.lang.ArchRule;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+import org.apache.isis.applib.annotation.DomainService;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+import lombok.experimental.UtilityClass;
+import lombok.val;
+
+/**
+ * A library of architecture tests to ensure coding conventions are followed for classes annotated with
+ * {@link DomainService}.
+ *
+ * @since 2.0 {@index}
+ */
+@UtilityClass
+public class ArchitectureDomainServiceRules {
+
+ /**
+ * This rule requires that classes annotated with the {@link DomainService} annotation must specify their
+ * {@link DomainService#logicalTypeName() logicalTypeName}.
+ */
+ public static ArchRule classes_annotated_with_DomainService_must_specify_logicalTypeName() {
+ return classes()
+ .that().areAnnotatedWith(DomainService.class)
+ .should().beAnnotatedWith(DomainService_logicalTypeName());
+ }
+
+ static DescribedPredicate<JavaAnnotation<?>> DomainService_logicalTypeName() {
+ return new DescribedPredicate<JavaAnnotation<?>>("@DomainService(logicalTypeName=...)") {
+ @Override public boolean apply(final JavaAnnotation<?> javaAnnotation) {
+ if (javaAnnotation.getRawType().isEquivalentTo(DomainService.class)) {
+ return false;
+ }
+ val properties = javaAnnotation.getProperties();
+ val value = properties.get("logicalTypeName");
+ return value instanceof String && ((String) value).length() > 0;
+ }
+ };
+ }
+
+ /**
+ * This rule requires that classes annotated with the {@link DomainObject} annotation must also be
+ * annotated with the {@link DomainObjectLayout} annotation.
+ */
+ public static ArchRule classes_annotated_with_DomainObject_must_also_be_annotated_with_DomainObjectLayout() {
+ return classes()
+ .that().areAnnotatedWith(DomainObject.class)
+ .should().beAnnotatedWith(DomainObjectLayout.class);
+ }
+
+
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java
new file mode 100644
index 0000000..662da9b
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java
@@ -0,0 +1,77 @@
+package org.apache.isis.testing.archtestsupport.applib.classrules;
+
+import com.tngtech.archunit.lang.ArchRule;
+
+import org.apache.isis.applib.annotation.DomainObject;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+import lombok.experimental.UtilityClass;
+
+/**
+ * A library of architecture tests to ensure coding conventions are followed for classes annotated with
+ * the JDO {@link javax.jdo.annotations.PersistenceCapable} annotation.
+ *
+ * @since 2.0 {@index}
+ */
+@UtilityClass
+public class ArchitectureJdoRules {
+
+ /**
+ * This rule requires that classes annotated with the JDO {@link javax.jdo.annotations.PersistenceCapable} annotation
+ * must also be annotated with the Apache Isis {@link DomainObject} annotation specifying that its
+ * {@link DomainObject#nature() nature} is an {@link org.apache.isis.applib.annotation.Nature#ENTITY entity}.
+ */
+ public static ArchRule classes_annotated_with_PersistenceCapable_must_also_be_annotated_with_DomainObject_nature_of_ENTITY() {
+ return classes()
+ .that().areAnnotatedWith(javax.jdo.annotations.PersistenceCapable.class)
+ .should().beAnnotatedWith(CommonDescribedPredicates.DomainObject_nature_ENTITY());
+ }
+
+ /**
+ * This rule requires that classes annotated with the JDO {@link javax.jdo.annotations.PersistenceCapable} annotation
+ * must also be annotated with the Apache Isis {@link javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter} annotation
+ * with a value of {@link org.apache.isis.applib.jaxb.PersistentEntityAdapter}<code>.class</code>.
+ *
+ * <p>
+ * Tnis is so that entities can be transparently referenced from XML-style view models.
+ * </p>
+ */
+ public static ArchRule classes_annotated_with_PersistenceCapable_must_also_be_annotated_with_XmlJavaAdapter() {
+ return classes()
+ .that().areAnnotatedWith(javax.jdo.annotations.PersistenceCapable.class)
+ .should().beAnnotatedWith(CommonDescribedPredicates.XmlJavaTypeAdapter_value_PersistentEntityAdapter());
+ }
+
+ /**
+ * This rule requires that classes annotated with the JDO {@link javax.jdo.annotations.PersistenceCapable} annotation
+ * must also implement {@link Comparable}.
+ *
+ * <p>
+ * This is so that entities have a natural ordering and can safely be added to parented collections of type
+ * {@link java.util.SortedSet}.
+ * </p>
+ */
+ public static ArchRule classes_annotated_with_PersistenceCapable_must_also_implement_Comparable() {
+ return classes()
+ .that().areAnnotatedWith(javax.jdo.annotations.PersistenceCapable.class)
+ .should().implement(Comparable.class);
+ }
+
+ /**
+ * This rule requires that classes annotated with the JDO {@link javax.jdo.annotations.PersistenceCapable} annotation
+ * must also be annotated with the JDO {@link javax.jdo.annotations.Uniques} or {@link javax.jdo.annotations.Unique}
+ * constraints.
+ *
+ * <p>
+ * This is so that entities will have an alternative business key in addition to the system-defined surrogate
+ * key.
+ * </p>
+ */
+ public static ArchRule classes_annotated_with_PersistenceCapable_must_also_be_annotated_with_Uniques_or_Unique() {
+ return classes()
+ .that().areAnnotatedWith(javax.jdo.annotations.PersistenceCapable.class)
+ .should().beAnnotatedWith(javax.jdo.annotations.Uniques.class)
+ .orShould().beAnnotatedWith(javax.jdo.annotations.Unique.class);
+ }
+
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJpaRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJpaRules.java
new file mode 100644
index 0000000..367f5ab
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJpaRules.java
@@ -0,0 +1,95 @@
+package org.apache.isis.testing.archtestsupport.applib.classrules;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.tngtech.archunit.base.DescribedPredicate;
+import com.tngtech.archunit.core.domain.JavaAnnotation;
+import com.tngtech.archunit.lang.ArchRule;
+
+import org.apache.isis.applib.annotation.DomainObject;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+import lombok.experimental.UtilityClass;
+import lombok.val;
+
+/**
+ * A library of architecture tests to ensure coding conventions are followed for classes annotated with
+ * the JPA {@link Entity} annotation.
+ *
+ * @since 2.0 {@index}
+ */
+@UtilityClass
+public class ArchitectureJpaRules {
+
+ /**
+ * This rule requires that classes annotated with the JPA {@link Entity} annotation must also be
+ * annotated with the Apache Isis {@link DomainObject} annotation specifying that its
+ * {@link DomainObject#nature() nature} is an {@link org.apache.isis.applib.annotation.Nature#ENTITY entity}.
+ */
+ public static ArchRule classes_annotated_with_Entity_must_also_be_annotated_with_DomainObject_nature_of_ENTITY() {
+ return classes()
+ .that().areAnnotatedWith(Entity.class)
+ .should().beAnnotatedWith(CommonDescribedPredicates.DomainObject_nature_ENTITY());
+ }
+
+ /**
+ * This rule requires that classes annotated with the JPA {@link Entity} annotation must also be
+ * annotated with the Apache Isis {@link javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter} annotation
+ * with a value of {@link org.apache.isis.applib.jaxb.PersistentEntityAdapter}<code>.class</code>.
+ *
+ * <p>
+ * Tnis is so that entities can be transparently referenced from XML-style view models.
+ * </p>
+ */
+ public static ArchRule classes_annotated_with_Entity_must_also_be_annotated_with_XmlJavaAdapter_PersistentEntityAdapter() {
+ return classes()
+ .that().areAnnotatedWith(Entity.class)
+ .should().beAnnotatedWith(CommonDescribedPredicates.XmlJavaTypeAdapter_value_PersistentEntityAdapter());
+ }
+
+ /**
+ * This rule requires that classes annotated with the JPA {@link Entity} annotation must also be
+ * implement {@link Comparable}.
+ *
+ * <p>
+ * This is so that entities have a natural ordering and can safely be added to parented collections of type
+ * {@link java.util.SortedSet}.
+ * </p>
+ */
+ public static ArchRule classes_annotated_with_Entity_must_also_implement_Comparable() {
+ return classes()
+ .that().areAnnotatedWith(Entity.class)
+ .should().implement(Comparable.class);
+ }
+
+ /**
+ * This rule requires that classes annotated with the JPA {@link Entity} annotation must also be annotated with the
+ * JPA {@link Table} annotation which includes {@link Table#uniqueConstraints() uniqueConstraints}.
+ *
+ * <p>
+ * This is so that entities will have an alternative business key in addition to the system-defined surrogate
+ * key.
+ * </p>
+ */
+ public static ArchRule classes_annotated_with_Entity_must_also_be_annotated_with_Table_with_uniqueConstraints() {
+ return classes()
+ .that().areAnnotatedWith(Entity.class)
+ .should().beAnnotatedWith(Table_uniqueConstraints());
+ }
+
+ private static DescribedPredicate<JavaAnnotation<?>> Table_uniqueConstraints() {
+ return new DescribedPredicate<JavaAnnotation<?>>("@Table(uniqueConstraints=...)") {
+ @Override public boolean apply(final JavaAnnotation<?> javaAnnotation) {
+ if (javaAnnotation.getRawType().isEquivalentTo(Table.class)) {
+ return false;
+ }
+ val properties = javaAnnotation.getProperties();
+ val uniqueConstraints = properties.get("uniqueConstraints");
+ return uniqueConstraints != null;
+ }
+ };
+ }
+
+
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/CommonDescribedPredicates.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/CommonDescribedPredicates.java
new file mode 100644
index 0000000..b57c556
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/CommonDescribedPredicates.java
@@ -0,0 +1,45 @@
+package org.apache.isis.testing.archtestsupport.applib.classrules;
+
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import com.tngtech.archunit.base.DescribedPredicate;
+import com.tngtech.archunit.core.domain.JavaAnnotation;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Nature;
+import org.apache.isis.applib.jaxb.PersistentEntityAdapter;
+
+import lombok.experimental.UtilityClass;
+import lombok.val;
+
+@UtilityClass
+public class CommonDescribedPredicates {
+
+ static DescribedPredicate<JavaAnnotation<?>> DomainObject_nature_ENTITY() {
+ return new DescribedPredicate<JavaAnnotation<?>>("@DomainObject(nature=ENTITY)") {
+ @Override public boolean apply(final JavaAnnotation<?> javaAnnotation) {
+ if (javaAnnotation.getRawType().isEquivalentTo(DomainObject.class)) {
+ return false;
+ }
+ val properties = javaAnnotation.getProperties();
+ val nature = properties.get("nature");
+ return nature == Nature.ENTITY;
+ }
+ };
+ }
+
+ static DescribedPredicate<JavaAnnotation<?>> XmlJavaTypeAdapter_value_PersistentEntityAdapter() {
+ return new DescribedPredicate<JavaAnnotation<?>>("@XmlJavaTypeAdapter(PersistentEntityAdapter.class)") {
+ @Override public boolean apply(final JavaAnnotation<?> javaAnnotation) {
+ if (javaAnnotation.getRawType().isEquivalentTo(XmlJavaTypeAdapter.class)) {
+ return false;
+ }
+ val properties = javaAnnotation.getProperties();
+ val value = properties.get("value");
+ return value == PersistentEntityAdapter.class;
+ }
+ };
+ }
+
+
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java
index 76520ea..d6ff0b8 100644
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java
@@ -1,5 +1,6 @@
package org.apache.isis.testing.archtestsupport.applib.modulerules;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -9,6 +10,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.annotation.Nullable;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.library.Architectures;
@@ -16,18 +20,28 @@ import com.tngtech.archunit.library.Architectures;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AnnotationUtils;
-import lombok.val;
import lombok.experimental.UtilityClass;
+import lombok.val;
-
+/**
+ * A library of architecture tests to ensure correct layering and usage of packages.
+ *
+ * @since 2.0 {@index}
+ */
@UtilityClass
public class ArchitectureModuleRules {
/**
- * Utility method.
+ * Utility method to aggregate the module classes (named "XxxModule") that are extracted from the
+ * {@link AnalyzeClasses} annotation on the provided class.
*
- * @param clazz
- * @return
+ * <p>
+ * The result is intended to be passed into {@link #code_dependencies_follow_module_Imports(List)} and
+ * {@link #code_dependencies_follow_module_Imports_and_subpackage_rules(List, List)}.
+ * </p>
+ *
+ * @see #code_dependencies_follow_module_Imports(List)
+ * @see #code_dependencies_follow_module_Imports_and_subpackage_rules(List, List)
*/
public static List<Class<?>> analyzeClasses_packagesOf(Class<?> clazz) {
val analyzeClassesAnnot = AnnotationUtils.findAnnotation(clazz, AnalyzeClasses.class);
@@ -39,38 +53,67 @@ public class ArchitectureModuleRules {
/**
* Ensures that the actual dependencies between classes within modules honour the module dependency graph inferred
* from the {@link Import} statements of each module.
+ *
+ * @see #code_dependencies_follow_module_Imports_and_subpackage_rules(List, List)
+ */
+ public static Architectures.LayeredArchitecture code_dependencies_follow_module_Imports(
+ List<Class<?>> moduleClasses) {
+
+ return code_dependencies_follow_module_Imports_and_subpackage_rules(moduleClasses, Collections.emptyList());
+ }
+
+ /**
+ * Ensures that the actual dependencies between classes within modules honour the module dependency graph inferred
+ * from the {@link Import} statements of each module AND also ensures that the subpackages within those rules only
+ * access the allowed subpackages of both their own &local;local&local; and the subpackages of other modules that
+ * they might reference.
+ *
+ * @see #code_dependencies_follow_module_Imports(List)
*/
- public static Architectures.LayeredArchitecture code_dependencies_follow_module_Imports(List<Class<?>> moduleClasses) {
+ public static Architectures.LayeredArchitecture code_dependencies_follow_module_Imports_and_subpackage_rules(
+ List<Class<?>> moduleClasses, List<Subpackage> subpackages) {
val layeredArchitecture = Architectures.layeredArchitecture();
- defineLayers(moduleClasses, layeredArchitecture);
+ defineLayers(moduleClasses, layeredArchitecture, subpackages);
val directDependenciesByImported = new HashMap<Class<?>, Set<Class<?>>>();
val directDependenciesByImporting = new HashMap<Class<?>, Set<Class<?>>>();
- computeDirectDependencies(moduleClasses, directDependenciesByImported, directDependenciesByImporting);
+ computeDirectDependencies(moduleClasses, directDependenciesByImported,
+ directDependenciesByImporting);
val transitiveDependenciesByImporting = new HashMap<Class<?>, Set<Class<?>>>();
- computeTransitiveDependencies(moduleClasses, directDependenciesByImporting, transitiveDependenciesByImporting);
+ computeTransitiveDependencies(moduleClasses, directDependenciesByImporting,
+ transitiveDependenciesByImporting);
val transitiveDependenciesByImported = invert(transitiveDependenciesByImporting);
- checkLayerAccess(layeredArchitecture, transitiveDependenciesByImported);
+ checkLayerAccess(layeredArchitecture, transitiveDependenciesByImported, subpackages);
- val importingClassesNotImported = new LinkedHashSet<Class<?>>(transitiveDependenciesByImporting.keySet());
+ val importingClassesNotImported = new LinkedHashSet<>(
+ transitiveDependenciesByImporting.keySet());
importingClassesNotImported.removeAll(transitiveDependenciesByImported.keySet());
checkNoAccessToTopmostLayers(layeredArchitecture, importingClassesNotImported);
return layeredArchitecture;
}
- private static void defineLayers(List<Class<?>> moduleClasses, Architectures.LayeredArchitecture layeredArchitecture) {
- moduleClasses.forEach(x -> {
- final String moduleName = nameOf(x);
- final String s = packageIdentifierFor(x);
- layeredArchitecture.layer(moduleName).definedBy(s);
+ private static void defineLayers(List<Class<?>> moduleClasses,
+ Architectures.LayeredArchitecture layeredArchitecture,
+ List<Subpackage> subpackages) {
+ moduleClasses.forEach(moduleClass -> {
+ layeredArchitecture.layer(nameOf(moduleClass)).definedBy(packageIdentifierFor(moduleClass));
+
+ subpackages
+ .stream()
+ .map(Subpackage::getName)
+ .forEach(subpackageName ->
+ layeredArchitecture.optionalLayer(nameOf(moduleClass, subpackageName))
+ .definedBy(packageIdentifierFor(moduleClass, subpackageName)));
});
}
- private static void computeDirectDependencies(List<Class<?>> moduleClasses, Map<Class<?>, Set<Class<?>>> directDependenciesByImported, Map<Class<?>, Set<Class<?>>> directDependenciesByImporting) {
+ private static void computeDirectDependencies(List<Class<?>> moduleClasses,
+ Map<Class<?>, Set<Class<?>>> directDependenciesByImported,
+ Map<Class<?>, Set<Class<?>>> directDependenciesByImporting) {
moduleClasses.forEach(
moduleClass -> {
final Import importAnnotation = AnnotationUtils.findAnnotation(moduleClass, Import.class);
@@ -80,7 +123,8 @@ public class ArchitectureModuleRules {
directDependenciesByImporting.put(moduleClass, importedClasses);
importedClasses.forEach(
importedClass -> {
- val importingClasses = directDependenciesByImported.computeIfAbsent(importedClass, k -> new LinkedHashSet<>());
+ val importingClasses = directDependenciesByImported
+ .computeIfAbsent(importedClass, k -> new LinkedHashSet<>());
importingClasses.add(moduleClass);
}
);
@@ -89,25 +133,52 @@ public class ArchitectureModuleRules {
);
}
- private static void computeTransitiveDependencies(List<Class<?>> moduleClasses, Map<Class<?>, Set<Class<?>>> directDependenciesByImporting, Map<Class<?>, Set<Class<?>>> transitiveDependenciesByImporting) {
+ private static void computeTransitiveDependencies(List<Class<?>> moduleClasses,
+ Map<Class<?>, Set<Class<?>>> directDependenciesByImporting,
+ Map<Class<?>, Set<Class<?>>> transitiveDependenciesByImporting) {
moduleClasses.forEach((moduleClass) -> {
val transitiveDependencies = new LinkedHashSet<Class<?>>();
- accumulateTransitiveDependencies(moduleClass, directDependenciesByImporting, transitiveDependencies);
+ accumulateTransitiveDependencies(moduleClass, directDependenciesByImporting,
+ transitiveDependencies);
transitiveDependenciesByImporting.put(moduleClass, transitiveDependencies);
});
}
- private static void checkLayerAccess(Architectures.LayeredArchitecture layeredArchitecture, Map<Class<?>, Set<Class<?>>> transitiveDependenciesByImported) {
- transitiveDependenciesByImported.forEach((importedClass, importingClasses) -> {
- final String importedModuleName = nameOf(importedClass);
- final String[] importingModuleNames = namesOf(importingClasses);
+ private static void checkLayerAccess(
+ Architectures.LayeredArchitecture layeredArchitecture,
+ Map<Class<?>, Set<Class<?>>> transitiveDependenciesByImported,
+ List<Subpackage> subpackages) {
+ transitiveDependenciesByImported.forEach((importedModule, importingModules) -> {
+
layeredArchitecture
- .whereLayer(importedModuleName)
- .mayOnlyBeAccessedByLayers(importingModuleNames);
+ // layering at the module level
+ .whereLayer(
+ nameOf(importedModule))
+ .mayOnlyBeAccessedByLayers(
+ namesOf(importingModules)
+ );
+
+ // in particular, access to subpackages
+ subpackages.forEach(subpackage -> {
+
+ val localModule = subpackage.mayBeAccessedBySubpackagesInSameModule().toArray(new String[]{});
+ val otherModules = subpackage.mayBeAccessedBySubpackagesInReferencingModules().toArray(new String[]{});
+ layeredArchitecture
+ .whereLayer(nameOf(importedModule, subpackage.getName()))
+ .mayOnlyBeAccessedByLayers(
+ both(
+ namesOf(importedModule, localModule),
+ namesOf(importingModules, otherModules)
+ )
+ );
+ });
+
});
}
- private static void checkNoAccessToTopmostLayers(Architectures.LayeredArchitecture layeredArchitecture, Set<Class<?>> importingClassesNotImported) {
+ private static void checkNoAccessToTopmostLayers(
+ Architectures.LayeredArchitecture layeredArchitecture,
+ Set<Class<?>> importingClassesNotImported) {
importingClassesNotImported.forEach(importingClass -> {
final String importingModuleName = nameOf(importingClass);
layeredArchitecture
@@ -117,18 +188,64 @@ public class ArchitectureModuleRules {
}
static String nameOf(Class<?> moduleClass) {
- return moduleClass.getSimpleName();
+ return nameOf(moduleClass, null);
+ }
+
+ static String nameOf(Class<?> moduleClass, @Nullable final String subpackageName) {
+ val simpleName = moduleClass.getSimpleName();
+ val moduleName = simpleName.replace("Module", "");
+ return moduleName + (subpackageName != null ? (" " + subpackageName) : "");
+ }
+
+ static String[] namesOf(Class<?> moduleClass, String... subpackageNames) {
+ val names = new ArrayList<String>();
+ if (subpackageNames == null) {
+ return null;
+ }
+ if (subpackageNames.length == 0 ||
+ subpackageNames.length == 1 && subpackageNames[0].equals("*")) {
+ names.add(nameOf(moduleClass));
+ } else {
+ Arrays.stream(subpackageNames).forEach(subpackageName ->
+ names.add(nameOf(moduleClass, subpackageName)));
+ }
+ return names.toArray(new String[] {});
+ }
+
+ static String[] namesOf(Set<Class<?>> importingClasses, @Nullable String... subpackageNames) {
+ val names = new ArrayList<String>();
+ importingClasses.forEach(importingClass -> {
+ if (subpackageNames == null || subpackageNames.length == 0) {
+ names.add(nameOf(importingClass));
+ } else {
+ Stream.of(subpackageNames).forEach(subpackageName ->
+ names.add(nameOf(importingClass, subpackageName)));
+ }
+ });
+ return names.toArray(new String[] {});
}
static String packageIdentifierFor(Class<?> moduleClass) {
- return moduleClass.getPackage().getName() + "..";
+ return packageIdentifierFor(moduleClass, null);
+ }
+
+ static String packageIdentifierFor(Class<?> moduleClass, @Nullable String subpackage) {
+ return moduleClass.getPackage().getName() + (subpackage != null ? ("." + subpackage) : "")
+ + "..";
+ }
+
+ static String[] both(String str, String[] arr) {
+ val strings = new ArrayList<String>();
+ strings.add(str);
+ strings.addAll(Arrays.asList(arr));
+ return strings.toArray(new String[] {});
}
- static String[] namesOf(Set<Class<?>> importingClasses) {
- return importingClasses.stream()
- .map(ArchitectureModuleRules::nameOf)
- .collect(Collectors.toList())
- .toArray(new String[]{});
+ static String[] both(String[] arr1, String[] arr2) {
+ val strings = new ArrayList<String>();
+ strings.addAll(Arrays.asList(arr1));
+ strings.addAll(Arrays.asList(arr2));
+ return strings.toArray(new String[] {});
}
static <T> Map<T, Set<T>> invert(Map<T, Set<T>> valueSetByKey) {
@@ -142,13 +259,15 @@ public class ArchitectureModuleRules {
}
static void accumulateTransitiveDependencies(
- final Class<?> referringClass
- , final Map<Class<?>, Set<Class<?>>> directDependenciesByReferringClass
- , final Set<Class<?>> transitiveDependenciesOfReferringClass) {
- val directDependencies = directDependenciesByReferringClass.getOrDefault(referringClass, Collections.emptySet());
+ final Class<?> referringClass,
+ final Map<Class<?>, Set<Class<?>>> directDependenciesByReferringClass,
+ final Set<Class<?>> transitiveDependenciesOfReferringClass) {
+ val directDependencies = directDependenciesByReferringClass
+ .getOrDefault(referringClass, Collections.emptySet());
transitiveDependenciesOfReferringClass.addAll(directDependencies);
directDependencies.forEach(directDependency ->
- accumulateTransitiveDependencies(directDependency, directDependenciesByReferringClass, transitiveDependenciesOfReferringClass));
+ accumulateTransitiveDependencies(directDependency, directDependenciesByReferringClass,
+ transitiveDependenciesOfReferringClass));
}
-}
+}
\ No newline at end of file
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/Subpackage.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/Subpackage.java
new file mode 100644
index 0000000..0cd810f
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/Subpackage.java
@@ -0,0 +1,48 @@
+package org.apache.isis.testing.archtestsupport.applib.modulerules;
+
+import java.util.List;
+
+/**
+ * Defines the rules for which subpackages or a module to check and whether the classes in those subpackages can access
+ * the classes in other subpackages either for the same module, or for modules that are referenced
+ * ({@link org.springframework.context.annotation.Import}ed) directly or transitively.
+ *
+ * @since 2.0 {@index}
+ */
+public interface Subpackage {
+
+ /**
+ * The name of the subpackage, for example "dom", "api", "spi" or "fixtures".
+ */
+ String getName();
+
+ /**
+ * A list of the (names of the) subpackages where classes in the same module as this package have access.
+ *
+ * <p>
+ * For example, the "dom" subpackage can probably be referenced from the "menu" subpackage,
+ * but not vice versa.
+ * </p>
+ *
+ * <p>
+ * The special value of "*" is a wildcard meaning that all subpackages (in the same module) can access.
+ * </p>
+ */
+ List<String> mayBeAccessedBySubpackagesInSameModule();
+
+ /**
+ * A list of the (names of the) subpackages where classes in the packages of other referencing modules may
+ * have access.
+ *
+ * <p>
+ * For example, in some cases the the "dom" subpackage may <i>not</i> be accessible from other
+ * modules if the intention is to require all programmatic access through an "api" subpackage
+ * (where the classes in <code>dom</code> implement interfaces defined in <code>api</code>).
+ * </p>
+ *
+ * <p>
+ * The special value of "*" is a wildcard meaning that all subpackages (in other modules) can access.
+ * </p>
+ */
+ List<String> mayBeAccessedBySubpackagesInReferencingModules();
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/SubpackageEnum.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/SubpackageEnum.java
new file mode 100644
index 0000000..8e2968a
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/SubpackageEnum.java
@@ -0,0 +1,169 @@
+package org.apache.isis.testing.archtestsupport.applib.modulerules;
+
+import java.util.List;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * A default implementation of {@link Subpackage} that constrains how (the classes in) subpackages across and within
+ * modules can reference either.
+ *
+ * @since 2.0 {@index}
+ */
+@RequiredArgsConstructor
+public enum SubpackageEnum implements Subpackage {
+
+ /**
+ * The <i>domain object model</i>module, containing the main business logic of the module.
+ *
+ * <p>
+ * Typically consists of entities and view models and associated repositories and stateless services used by
+ * those entities and view models.
+ * </p>
+ */
+ dom(
+ singletonList("*"), // wildcard means that all subpackages in this module can access 'dom'
+ singletonList("*") // wildcard means that all subpackages in other modules can access 'dom'
+ ),
+ /**
+ * Optional, constitutes a formal API to the module.
+ *
+ * <p>
+ * If used, then access to the <i>dom</i> module is likely to be restricted to only the subpackages of its
+ * own "local" module with no access granted to subpackages of other "external"
+ * referencing modules.
+ * </p>
+ */
+ api(
+ singletonList("*"), // wildcard means that all subpackages in this module can access 'api'
+ singletonList("*") // wildcard means that all subpackages in other modules can access 'api'
+ ),
+ /**
+ * Optional, but if used then will hold view model that implement some sort of assisted business process, for
+ * example dashboards, or filtering.
+ */
+ app(
+ asList("fixtures", "integtests"), // only tests should call
+ asList("fixtures", "integtests") // only tests should call
+ ),
+ /**
+ * Holds the menus that are visible in the UI.
+ *
+ * <p>
+ * These should only be called by the framework, not programmatically (except for tests).
+ * </p>
+ */
+ menu(
+ asList("fixtures", "integtests"), // only tests should call
+ asList("fixtures", "integtests") // only tests should call
+ ),
+ /**
+ * Holds mixins that contribute functionality to OTHER modules.
+ *
+ * <p>
+ * Mixins are one of the main techniques for decoupling dependencies between modules.
+ * </p>
+ *
+ * <p>
+ * Note that mixins to THIS module normally would just live in the <i>dom</i> subpackage, eg <i>dom.mixins</i>.
+ * </p>
+ */
+ contributions(
+ asList("subscribers", // may subscribe to events fired by mixins
+ "fixtures", "integtests"), // tests can also call
+ asList("subscribers", // may subscribe to events fired by mixins
+ "fixtures", "integtests") // tests can also call
+ ),
+ /**
+ * Holds domain services that subscribe to events fired from OTHER modules.
+ *
+ * <p>
+ * Subscriptions is the other main technique for decoupling dependencies between modules.
+ * </p>
+ */
+ subscribers(
+ asList("fixtures", "integtests"), // only tests should call
+ asList("fixtures", "integtests") // only tests should call
+ ),
+ /**
+ * Holds the menus that are visible as the REST endpoints.
+ *
+ * <p>
+ * These should only be called by the framework, not programmatically (except for tests).
+ * </p>
+ */
+ restapi(
+ asList("fixtures", "integtests"), // only tests should call
+ asList("fixtures", "integtests") // only tests should call
+ ),
+ /**
+ * Define an interface for OTHER modules to implement; this is therefore an alternative and
+ * more structured way to decoupled modules.
+ *
+ * <p>
+ * With events, the module that emits the event doesn't know much about what the action to be
+ * performed in the other module might be. But if we use an SPI, then this module will call
+ * all implementations of the SPI at certain well-defined points; so it kind of is like a
+ * lifecycle sort of thing.
+ * </p>
+ *
+ * <p>
+ * Also, the SPI's interface could be more exotic, ie "fatter" than the simple event.
+ * </p>
+ */
+ spi(
+ asList("dom", "contributions", "subscribers"), // callers of a module's own SPI
+ singletonList("spiimpl") // other modules should only implement the SPI
+ ),
+ /**
+ * These are this module's implementations of OTHER modules' SPI services.
+ */
+ spiimpl(
+ asList("fixtures", "integtests"), // only tests should call
+ asList("fixtures", "integtests") // only tests should call
+ ),
+ /**
+ * Fixture scripts used to setup the systen when prototyping and for integ tests.
+ */
+ fixtures(
+ asList("seed", "integtests"), // no restrictions
+ asList("seed", "integtests") // no restrictions
+ ),
+ /**
+ * Seed scripts used to setup the systen, for example reference data).
+ */
+ seed(
+ emptyList(), // should not be called directly
+ emptyList() // should not be called directly
+ ),
+ integtests(
+ emptyList(), // should not be called directly
+ emptyList() // should not be called directly
+ ),
+ ;
+
+ final List<String> local;
+ final List<String> referencing;
+
+ public String getName() {
+ return name();
+ }
+
+ @Override
+ public List<String> mayBeAccessedBySubpackagesInSameModule() {
+ return local;
+ }
+
+ @Override
+ public List<String> mayBeAccessedBySubpackagesInReferencingModules() {
+ return referencing;
+ }
+
+ private static String[] asArray(List<String> list) {
+ return list != null ?
+ list.toArray(new String[] {}) : null;
+ }
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/ArchitecturePackageRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/ArchitecturePackageRules.java
deleted file mode 100644
index bdd6091..0000000
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/ArchitecturePackageRules.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.apache.isis.testing.archtestsupport.applib.packagerules;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import com.tngtech.archunit.lang.ArchRule;
-import com.tngtech.archunit.library.Architectures;
-
-import lombok.val;
-import lombok.experimental.UtilityClass;
-
-@UtilityClass
-public class ArchitecturePackageRules {
-
- public static ArchRule code_dependencies_follow_module_subpackages(Class<?> moduleClass, List<Subpackage> subpackages) {
- val layeredArchitecture = Architectures.layeredArchitecture();
- defineAndCheckSubpackageDependencies(moduleClass, layeredArchitecture, subpackages);
- return layeredArchitecture;
- }
-
- void defineAndCheckSubpackageDependencies(Class<?> moduleClass, Architectures.LayeredArchitecture layeredArchitecture, List<Subpackage> subpackages) {
- subpackages.forEach(subpackage -> subpackage.defineLayer(layeredArchitecture, moduleClass));
-
- subpackages.forEach(referenced -> {
- final String[] referencingSubpackageNames =
- subpackages.stream().filter(subpackage -> subpackage.canReference(referenced))
- .map(Subpackage::getName)
- .collect(Collectors.toList()).toArray(new String[]{});
- layeredArchitecture.whereLayer(referenced.getName()).mayOnlyBeAccessedByLayers(referencingSubpackageNames);
- });
- }
-
-}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/Subpackage.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/Subpackage.java
deleted file mode 100644
index b609683..0000000
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/Subpackage.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.apache.isis.testing.archtestsupport.applib.packagerules;
-
-import com.tngtech.archunit.library.Architectures;
-
-import lombok.val;
-
-public interface Subpackage {
- String getName();
-
- SubpackageType getSubpackageType();
-
- String packageIdentifierWithin(Class<?> moduleClass);
-
- default void defineLayer(Architectures.LayeredArchitecture layeredArchitecture, Class<?> moduleClass) {
- val layerDefinition = getSubpackageType().defineLayer(this, moduleClass, layeredArchitecture);
- layerDefinition.definedBy(packageIdentifierWithin(moduleClass));
- }
-
- boolean canReference(Subpackage referenced);
-}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageEnum.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageEnum.java
deleted file mode 100644
index cd2c614..0000000
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageEnum.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.apache.isis.testing.archtestsupport.applib.packagerules;
-
-import java.util.List;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
-
-import static org.apache.isis.testing.archtestsupport.applib.packagerules.SubpackageType.MANDATORY;
-import static org.apache.isis.testing.archtestsupport.applib.packagerules.SubpackageType.OPTIONAL;
-
-import lombok.Getter;
-
-
-public enum SubpackageEnum implements Subpackage {
-
- parent(MANDATORY, emptyList(), "fixtures") {
- @Override
- public String packageIdentifierWithin(Class<?> moduleClass) {
- return moduleClass.getPackage().getName() + "..";
- }
- },
-
- dom(OPTIONAL, emptyList(), "fixtures"), // allow access to personas
-
- app(OPTIONAL, singletonList(dom)),
-
- menu(OPTIONAL, singletonList(dom)),
-
- contributions(OPTIONAL, singletonList(dom)),
-
- subscriptions(OPTIONAL, singletonList(dom)),
-
- restapi(OPTIONAL, singletonList(dom)),
-
- spi(OPTIONAL, singletonList(dom)),
-
- spiimpl(OPTIONAL, singletonList(dom)),
-
- fixtures(OPTIONAL, asList(dom, menu, contributions)),
-
- seed(OPTIONAL, asList(dom, fixtures)),
-
- integtests(OPTIONAL, asList(dom, fixtures, app, menu, contributions, subscriptions, restapi, spi, spiimpl, seed)),
- ;
-
- @Getter
- final SubpackageType subpackageType;
- final List<Subpackage> references;
- final List<String> softReferences;
-
- SubpackageEnum(SubpackageType subpackageType, List<Subpackage> references, String... softReferences) {
- this.subpackageType = subpackageType;
- this.references = references;
- this.softReferences = asList(softReferences);
- }
-
- public String getName() {
- return name();
- }
-
- public String packageIdentifierWithin(Class<?> moduleClass) {
- return moduleClass.getPackage().getName() + "." + name() + "..";
- }
-
- public boolean canReference(Subpackage subpackage) {
- return references.contains(subpackage) || softReferences.contains(subpackage.getName());
- }
-}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageType.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageType.java
deleted file mode 100644
index cfc7a90..0000000
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageType.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.apache.isis.testing.archtestsupport.applib.packagerules;
-
-import com.tngtech.archunit.library.Architectures;
-
-public enum SubpackageType {
- MANDATORY {
- @Override
- Architectures.LayeredArchitecture.LayerDefinition defineLayer(
- Subpackage subpackage, Class<?> moduleClass, Architectures.LayeredArchitecture layeredArchitecture) {
- return layeredArchitecture.layer(subpackage.getName());
- }
- },
- OPTIONAL {
- @Override
- Architectures.LayeredArchitecture.LayerDefinition defineLayer(
- Subpackage subpackage, Class<?> moduleClass, Architectures.LayeredArchitecture layeredArchitecture) {
- return layeredArchitecture.optionalLayer(subpackage.getName());
- }
- };
-
- abstract Architectures.LayeredArchitecture.LayerDefinition defineLayer(Subpackage subpackage, Class<?> moduleClass, Architectures.LayeredArchitecture layeredArchitecture);
-}