GH-862 - Trigger jMolecules architecture verifications if present on the classpath.
This commit is contained in:
@@ -20,6 +20,7 @@ import static com.tngtech.archunit.core.domain.JavaClass.Predicates.*;
|
||||
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.*;
|
||||
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.*;
|
||||
import static java.util.stream.Collectors.*;
|
||||
import static org.springframework.modulith.core.Violations.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -34,12 +35,10 @@ import org.jgrapht.Graph;
|
||||
import org.jgrapht.graph.DefaultDirectedGraph;
|
||||
import org.jgrapht.graph.DefaultEdge;
|
||||
import org.jgrapht.traverse.TopologicalOrderIterator;
|
||||
import org.jmolecules.archunit.JMoleculesDddRules;
|
||||
import org.springframework.aot.generate.Generated;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.modulith.core.Types.JMoleculesTypes;
|
||||
import org.springframework.modulith.core.Violations.Violation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
@@ -461,24 +460,22 @@ public class ApplicationModules implements Iterable<ApplicationModule> {
|
||||
*/
|
||||
public Violations detectViolations() {
|
||||
|
||||
Violations violations = rootPackages.stream() //
|
||||
var cycleViolations = rootPackages.stream() //
|
||||
.map(this::assertNoCyclesFor) //
|
||||
.flatMap(it -> it.getDetails().stream()) //
|
||||
.map(Violation::new) //
|
||||
.collect(Violations.toViolations());
|
||||
.collect(toViolations());
|
||||
|
||||
if (JMoleculesTypes.areRulesPresent()) {
|
||||
var jMoleculesViolations = JMoleculesTypes.getRules().stream()
|
||||
.map(it -> it.evaluate(allClasses))
|
||||
.map(EvaluationResult::getFailureReport)
|
||||
.flatMap(it -> it.getDetails().stream())
|
||||
.collect(toViolations());
|
||||
|
||||
EvaluationResult result = JMoleculesDddRules.all().evaluate(allClasses);
|
||||
|
||||
for (String message : result.getFailureReport().getDetails()) {
|
||||
violations = violations.and(message);
|
||||
}
|
||||
}
|
||||
|
||||
return Stream.concat(rootModules.get().stream(), modules.values().stream()) //
|
||||
var dependencyViolations = Stream.concat(rootModules.get().stream(), modules.values().stream()) //
|
||||
.map(it -> it.detectDependencies(this)) //
|
||||
.reduce(violations, Violations::and);
|
||||
.reduce(NONE, Violations::and);
|
||||
|
||||
return cycleViolations.and(jMoleculesViolations).and(dependencyViolations);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,8 +19,12 @@ import static com.tngtech.archunit.core.domain.JavaClass.Predicates.*;
|
||||
import static org.springframework.modulith.core.SyntacticSugar.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jmolecules.archunit.JMoleculesArchitectureRules;
|
||||
import org.jmolecules.archunit.JMoleculesDddRules;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.modulith.PackageInfo;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -29,6 +33,7 @@ import org.springframework.util.ClassUtils;
|
||||
import com.tngtech.archunit.base.DescribedPredicate;
|
||||
import com.tngtech.archunit.core.domain.JavaClass;
|
||||
import com.tngtech.archunit.core.domain.JavaMethod;
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
|
||||
/**
|
||||
* @author Oliver Drotbohm
|
||||
@@ -49,9 +54,14 @@ class Types {
|
||||
private static final String BASE_PACKAGE = "org.jmolecules";
|
||||
private static final String ANNOTATION_PACKAGE = BASE_PACKAGE + ".ddd.annotation";
|
||||
private static final String AT_ENTITY = ANNOTATION_PACKAGE + ".Entity";
|
||||
private static final String ARCHUNIT_RULES = BASE_PACKAGE + ".archunit.JMoleculesDddRules";
|
||||
private static final String MODULE = ANNOTATION_PACKAGE + ".Module";
|
||||
|
||||
private static final String DDD_RULES = BASE_PACKAGE + ".archunit.JMoleculesDddRules";
|
||||
private static final String ARCHITECTURE_RULES = BASE_PACKAGE + ".archunit.JMoleculesArchitectureRules";
|
||||
private static final String HEXAGONAL = BASE_PACKAGE + ".architecture.hexagonal.Port";
|
||||
private static final String LAYERED = BASE_PACKAGE + ".architecture.layered.InfrastructureLayer";
|
||||
private static final String ONION = BASE_PACKAGE + ".architecture.onion.classical.InfrastructureRing";
|
||||
|
||||
private static final boolean PRESENT = ClassUtils.isPresent(AT_ENTITY, JMoleculesTypes.class.getClassLoader());
|
||||
private static final boolean MODULE_PRESENT = ClassUtils.isPresent(MODULE, JMoleculesTypes.class.getClassLoader());
|
||||
|
||||
@@ -89,8 +99,38 @@ class Types {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean areRulesPresent() {
|
||||
return ClassUtils.isPresent(ARCHUNIT_RULES, JMoleculesTypes.class.getClassLoader());
|
||||
/**
|
||||
* Returns all architectural rules to enforce depending on the classpath arrangement.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
public static Collection<ArchRule> getRules() {
|
||||
|
||||
var classLoader = JMoleculesTypes.class.getClassLoader();
|
||||
var rules = new ArrayList<ArchRule>();
|
||||
|
||||
if (ClassUtils.isPresent(DDD_RULES, classLoader)) {
|
||||
rules.add(JMoleculesDddRules.all());
|
||||
}
|
||||
|
||||
if (!ClassUtils.isPresent(ARCHITECTURE_RULES, classLoader)) {
|
||||
return rules;
|
||||
}
|
||||
|
||||
if (ClassUtils.isPresent(HEXAGONAL, classLoader)) {
|
||||
rules.add(JMoleculesArchitectureRules.ensureHexagonal());
|
||||
}
|
||||
|
||||
if (ClassUtils.isPresent(LAYERED, classLoader)) {
|
||||
rules.add(JMoleculesArchitectureRules.ensureLayering());
|
||||
}
|
||||
|
||||
if (ClassUtils.isPresent(ONION, classLoader)) {
|
||||
rules.add(JMoleculesArchitectureRules.ensureOnionClassical());
|
||||
rules.add(JMoleculesArchitectureRules.ensureOnionSimple());
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.modulith.core;
|
||||
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -49,13 +51,13 @@ public class Violations extends RuntimeException {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Collector} to turn a {@link java.util.stream.Stream} of {@link RuntimeException}s into a
|
||||
* {@link Violations} instance.
|
||||
* A {@link Collector} to turn a {@link java.util.stream.Stream} of {@link String}s into a {@link Violations}
|
||||
* instance.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
static Collector<Violation, ?, Violations> toViolations() {
|
||||
return Collectors.collectingAndThen(Collectors.toList(), Violations::new);
|
||||
static Collector<String, ?, Violations> toViolations() {
|
||||
return mapping(Violation::new, collectingAndThen(Collectors.toList(), Violations::new));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -110,13 +112,18 @@ public class Violations extends RuntimeException {
|
||||
*/
|
||||
Violations and(Violation violation) {
|
||||
|
||||
Assert.notNull(violation, "Exception must not be null!");
|
||||
Assert.notNull(violation, "Violation must not be null!");
|
||||
|
||||
return new Violations(unionByMessage(violations, List.of(violation)));
|
||||
return and(new Violations(List.of(violation)));
|
||||
}
|
||||
|
||||
Violations and(Violations other) {
|
||||
return new Violations(unionByMessage(violations, other.violations));
|
||||
Violations and(Violations that) {
|
||||
|
||||
Assert.notNull(that, "Violations must not be null!");
|
||||
|
||||
return hasViolations() || that.hasViolations()
|
||||
? new Violations(unionByMessage(violations, that.violations))
|
||||
: NONE;
|
||||
}
|
||||
|
||||
Violations and(String violation) {
|
||||
|
||||
@@ -29,4 +29,4 @@ Dependencies into internals of xref:fundamentals.adoc#modules.advanced.open[Open
|
||||
If those are configured, dependencies to other application modules are rejected.
|
||||
See xref:fundamentals.adoc#modules.explicit-dependencies[Explicit Application Module Dependencies] and xref:fundamentals.adoc#modules.named-interfaces[Named Interfaces] for details.
|
||||
|
||||
Spring Modulith optionally integrates with the jMolecules ArchUnit library and, if present, automatically triggers its Domain-Driven Design verification rules described https://github.com/xmolecules/jmolecules-integrations/tree/main/jmolecules-archunit[here].
|
||||
Spring Modulith optionally integrates with the jMolecules ArchUnit library and, if present, automatically triggers its Domain-Driven Design and architectural verification rules described https://github.com/xmolecules/jmolecules-integrations/tree/main/jmolecules-archunit[here].
|
||||
|
||||
Reference in New Issue
Block a user