diff --git a/jmolecules-archunit/src/main/java/org/jmolecules/archunit/JMoleculesDddRules.java b/jmolecules-archunit/src/main/java/org/jmolecules/archunit/JMoleculesDddRules.java index 7500046..d76a1c1 100644 --- a/jmolecules-archunit/src/main/java/org/jmolecules/archunit/JMoleculesDddRules.java +++ b/jmolecules-archunit/src/main/java/org/jmolecules/archunit/JMoleculesDddRules.java @@ -35,6 +35,8 @@ import com.tngtech.archunit.base.DescribedPredicate; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaField; +import com.tngtech.archunit.core.domain.JavaParameterizedType; +import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.domain.properties.CanBeAnnotated; import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ArchRule; @@ -58,7 +60,8 @@ * @author Oliver Drotbohm * @author Torsten Juergeleit * @author Hasan Kara - * @see Advancing Enterprise DDD - Reinstating the Aggregate + * @see Advancing Enterprise DDD - Reinstating the + * Aggregate */ public class JMoleculesDddRules { @@ -146,7 +149,8 @@ public static ArchRule entitiesShouldBeDeclaredForUseInSameAggregate() { public static ArchRule aggregateReferencesShouldBeViaIdOrAssociation() { DescribedPredicate referenceAnAggregateRoot = areAssignableTo(AggregateRoot.class) - .or(hasFieldTypeAnnotatedWith(org.jmolecules.ddd.annotation.AggregateRoot.class)); + .or(hasFieldTypeAnnotatedWith(org.jmolecules.ddd.annotation.AggregateRoot.class)) + .or(hasParameterizedFieldOfTypeAnnotatedWith(org.jmolecules.ddd.annotation.AggregateRoot.class)); return ArchRuleDefinition.fields() // .that(new OwnerMatches(IS_IDENTIFIABLE).and(referenceAnAggregateRoot)) // @@ -194,6 +198,11 @@ private static FieldTypeIsAnnotatedWith hasFieldTypeAnnotatedWith(Class hasParameterizedFieldOfTypeAnnotatedWith( + Class type) { + return new ParameterizedFieldOfTypeAnnotatedWith(type); + } + private static class IsDeclaredToUseTheSameAggregate extends ArchCondition { private static final ResolvableType COLLECTION_TYPE = ResolvableType.forClass(Collection.class); @@ -284,6 +293,37 @@ public boolean test(JavaField input) { } } + private static class ParameterizedFieldOfTypeAnnotatedWith extends DescribedPredicate { + + private final DescribedPredicate isAnnotatedWith; + + public ParameterizedFieldOfTypeAnnotatedWith(Class type) { + + super("is collection of type annotated with %s", type.getSimpleName()); + + this.isAnnotatedWith = CanBeAnnotated.Predicates.annotatedWith(type); + } + + /* + * (non-Javadoc) + * @see java.util.function.Predicate#test(java.lang.Object) + */ + @Override + public boolean test(JavaField input) { + + if (!(input.getType() instanceof JavaParameterizedType)) { + return false; + } + + JavaParameterizedType parameterizedType = (JavaParameterizedType) input.getType(); + + return parameterizedType.getActualTypeArguments() + .stream() + .map(JavaType::toErasure) + .anyMatch(isAnnotatedWith); + } + } + private static class IsAssignableTypeField extends DescribedPredicate { private static final ResolvableType COLLECTION_TYPE = ResolvableType.forClass(Collection.class); diff --git a/jmolecules-archunit/src/test/java/org/jmolecules/archunit/JMoleculesDddRulesUnitTest.java b/jmolecules-archunit/src/test/java/org/jmolecules/archunit/JMoleculesDddRulesUnitTest.java index 9bdf668..1bd87db 100644 --- a/jmolecules-archunit/src/test/java/org/jmolecules/archunit/JMoleculesDddRulesUnitTest.java +++ b/jmolecules-archunit/src/test/java/org/jmolecules/archunit/JMoleculesDddRulesUnitTest.java @@ -65,7 +65,11 @@ void detectsViolations(JavaClasses classes) { violation(SampleValueObject.class, "annotatedEntity", AnnotatedEntity.class, null), violation(SampleValueObject.class, "aggregate", SampleAggregate.class, null), violation(SampleValueObject.class, "annotatedAggregate", AnnotatedAggregate.class, null), - violation(SampleGrandChildEntity.class, "otherEntity", OtherEntity.class, OtherAggregate.class) // GH-222 + violation(SampleGrandChildEntity.class, "otherEntity", OtherEntity.class, OtherAggregate.class), // GH-222 + violation(OtherAnnotatedAggregate.class, "invalidAnnotatedAggregate", AnnotatedAggregate.class, null), // + violation(OtherAnnotatedAggregate.class, "invalidAnnotatedAggregateInCollection", Collection.class, + Association.class), // + violation(OtherAnnotatedAggregate.class, "invalidAnnotatedAggregateInMap", Map.class, Association.class) // ); } @@ -123,4 +127,20 @@ static class SampleValueObject implements ValueObject { SampleAggregate aggregate; AnnotatedAggregate annotatedAggregate; } + + @org.jmolecules.ddd.annotation.AggregateRoot + static class OtherAnnotatedAggregate { + + @org.jmolecules.ddd.annotation.Identity Long id; + AnnotatedAggregate invalidAnnotatedAggregate; + Collection invalidAnnotatedAggregateInCollection; + Map invalidAnnotatedAggregateInMap; + } + + @org.jmolecules.ddd.annotation.AggregateRoot + static class ThirdAnnotatedAggregate { + + @org.jmolecules.ddd.annotation.Identity Long id; + AnnotatedEntity valid; + } }