Exercise 8 PR #95
|
@ -62,6 +62,12 @@
|
||||||
<artifactId>mockito-core</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<version>2.21.0</version>
|
<version>2.21.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tngtech.archunit</groupId>
|
||||||
|
<artifactId>archunit-junit4</artifactId>
|
||||||
|
<version>0.22.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package ch.unisg.tapastasks;
|
||||||
|
|
||||||
|
import ch.unisg.tapastasks.archunit.HexagonalArchitecture;
|
||||||
|
import com.tngtech.archunit.core.importer.ClassFileImporter;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
|
||||||
|
|
||||||
|
class DependencyRuleTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPackageDependencies() {
|
||||||
|
noClasses()
|
||||||
|
.that()
|
||||||
|
.resideInAPackage("ch.unisg.tapastasks.tasks.domain..")
|
||||||
|
.should()
|
||||||
|
.dependOnClassesThat()
|
||||||
|
.resideInAnyPackage("ch.unisg.tapastasks.tasks.application..")
|
||||||
|
.check(new ClassFileImporter()
|
||||||
|
.importPackages("ch.unisg.tapastasks.tasks.."));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package ch.unisg.tapastasks.archunit;
|
||||||
|
|
||||||
|
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Adapters extends ArchitectureElement {
|
||||||
|
|
||||||
|
private final HexagonalArchitecture parentContext;
|
||||||
|
private List<String> incomingAdapterPackages = new ArrayList<>();
|
||||||
|
private List<String> outgoingAdapterPackages = new ArrayList<>();
|
||||||
|
|
||||||
|
Adapters(HexagonalArchitecture parentContext, String basePackage) {
|
||||||
|
super(basePackage);
|
||||||
|
this.parentContext = parentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Adapters outgoing(String packageName) {
|
||||||
|
this.incomingAdapterPackages.add(fullQualifiedPackage(packageName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Adapters incoming(String packageName) {
|
||||||
|
this.outgoingAdapterPackages.add(fullQualifiedPackage(packageName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> allAdapterPackages() {
|
||||||
|
List<String> allAdapters = new ArrayList<>();
|
||||||
|
allAdapters.addAll(incomingAdapterPackages);
|
||||||
|
allAdapters.addAll(outgoingAdapterPackages);
|
||||||
|
return allAdapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HexagonalArchitecture and() {
|
||||||
|
return parentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getBasePackage() {
|
||||||
|
return basePackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dontDependOnEachOther(JavaClasses classes) {
|
||||||
|
List<String> allAdapters = allAdapterPackages();
|
||||||
|
for (String adapter1 : allAdapters) {
|
||||||
|
for (String adapter2 : allAdapters) {
|
||||||
|
if (!adapter1.equals(adapter2)) {
|
||||||
|
denyDependency(adapter1, adapter2, classes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void doesNotDependOn(String packageName, JavaClasses classes) {
|
||||||
|
denyDependency(this.basePackage, packageName, classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void doesNotContainEmptyPackages() {
|
||||||
|
denyEmptyPackages(allAdapterPackages());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package ch.unisg.tapastasks.archunit;
|
||||||
|
|
||||||
|
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ApplicationLayer extends ArchitectureElement {
|
||||||
|
|
||||||
|
private final HexagonalArchitecture parentContext;
|
||||||
|
private List<String> incomingPortsPackages = new ArrayList<>();
|
||||||
|
private List<String> outgoingPortsPackages = new ArrayList<>();
|
||||||
|
private List<String> servicePackages = new ArrayList<>();
|
||||||
|
|
||||||
|
public ApplicationLayer(String basePackage, HexagonalArchitecture parentContext) {
|
||||||
|
super(basePackage);
|
||||||
|
this.parentContext = parentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationLayer incomingPorts(String packageName) {
|
||||||
|
this.incomingPortsPackages.add(fullQualifiedPackage(packageName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationLayer outgoingPorts(String packageName) {
|
||||||
|
this.outgoingPortsPackages.add(fullQualifiedPackage(packageName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationLayer services(String packageName) {
|
||||||
|
this.servicePackages.add(fullQualifiedPackage(packageName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HexagonalArchitecture and() {
|
||||||
|
return parentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doesNotDependOn(String packageName, JavaClasses classes) {
|
||||||
|
denyDependency(this.basePackage, packageName, classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incomingAndOutgoingPortsDoNotDependOnEachOther(JavaClasses classes) {
|
||||||
|
denyAnyDependency(this.incomingPortsPackages, this.outgoingPortsPackages, classes);
|
||||||
|
denyAnyDependency(this.outgoingPortsPackages, this.incomingPortsPackages, classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> allPackages() {
|
||||||
|
List<String> allPackages = new ArrayList<>();
|
||||||
|
allPackages.addAll(incomingPortsPackages);
|
||||||
|
allPackages.addAll(outgoingPortsPackages);
|
||||||
|
allPackages.addAll(servicePackages);
|
||||||
|
return allPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doesNotContainEmptyPackages() {
|
||||||
|
denyEmptyPackages(allPackages());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package ch.unisg.tapastasks.archunit;
|
||||||
|
|
||||||
|
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||||
|
import com.tngtech.archunit.core.importer.ClassFileImporter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.tngtech.archunit.base.DescribedPredicate.*;
|
||||||
|
import static com.tngtech.archunit.base.DescribedPredicate.greaterThanOrEqualTo;
|
||||||
|
import static com.tngtech.archunit.lang.conditions.ArchConditions.*;
|
||||||
|
import static com.tngtech.archunit.lang.conditions.ArchConditions.containNumberOfElements;
|
||||||
|
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;
|
||||||
|
|
||||||
|
abstract class ArchitectureElement {
|
||||||
|
|
||||||
|
final String basePackage;
|
||||||
|
|
||||||
|
public ArchitectureElement(String basePackage) {
|
||||||
|
this.basePackage = basePackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fullQualifiedPackage(String relativePackage) {
|
||||||
|
return this.basePackage + "." + relativePackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void denyDependency(String fromPackageName, String toPackageName, JavaClasses classes) {
|
||||||
|
noClasses()
|
||||||
|
.that()
|
||||||
|
.resideInAPackage("ch.unisg.tapastasks.tasks.domain..")
|
||||||
|
.should()
|
||||||
|
.dependOnClassesThat()
|
||||||
|
.resideInAnyPackage("ch.unisg.tapastasks.tasks.application..")
|
||||||
|
.check(classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void denyAnyDependency(
|
||||||
|
List<String> fromPackages, List<String> toPackages, JavaClasses classes) {
|
||||||
|
for (String fromPackage : fromPackages) {
|
||||||
|
for (String toPackage : toPackages) {
|
||||||
|
noClasses()
|
||||||
|
.that()
|
||||||
|
.resideInAPackage(matchAllClassesInPackage(fromPackage))
|
||||||
|
.should()
|
||||||
|
.dependOnClassesThat()
|
||||||
|
.resideInAnyPackage(matchAllClassesInPackage(toPackage))
|
||||||
|
.check(classes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String matchAllClassesInPackage(String packageName) {
|
||||||
|
return packageName + "..";
|
||||||
|
}
|
||||||
|
|
||||||
|
void denyEmptyPackage(String packageName) {
|
||||||
|
classes()
|
||||||
|
.that()
|
||||||
|
.resideInAPackage(matchAllClassesInPackage(packageName))
|
||||||
|
.should(containNumberOfElements(greaterThanOrEqualTo(1)))
|
||||||
|
.check(classesInPackage(packageName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private JavaClasses classesInPackage(String packageName) {
|
||||||
|
return new ClassFileImporter().importPackages(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void denyEmptyPackages(List<String> packages) {
|
||||||
|
for (String packageName : packages) {
|
||||||
|
denyEmptyPackage(packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package ch.unisg.tapastasks.archunit;
|
||||||
|
|
||||||
|
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HexagonalArchitecture extends ArchitectureElement {
|
||||||
|
|
||||||
|
private Adapters adapters;
|
||||||
|
private ApplicationLayer applicationLayer;
|
||||||
|
private String configurationPackage;
|
||||||
|
private List<String> domainPackages = new ArrayList<>();
|
||||||
|
|
||||||
|
public static HexagonalArchitecture boundedContext(String basePackage) {
|
||||||
|
return new HexagonalArchitecture(basePackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HexagonalArchitecture(String basePackage) {
|
||||||
|
super(basePackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Adapters withAdaptersLayer(String adaptersPackage) {
|
||||||
|
this.adapters = new Adapters(this, fullQualifiedPackage(adaptersPackage));
|
||||||
|
return this.adapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HexagonalArchitecture withDomainLayer(String domainPackage) {
|
||||||
|
this.domainPackages.add(fullQualifiedPackage(domainPackage));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationLayer withApplicationLayer(String applicationPackage) {
|
||||||
|
this.applicationLayer = new ApplicationLayer(fullQualifiedPackage(applicationPackage), this);
|
||||||
|
return this.applicationLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HexagonalArchitecture withConfiguration(String packageName) {
|
||||||
|
this.configurationPackage = fullQualifiedPackage(packageName);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void domainDoesNotDependOnOtherPackages(JavaClasses classes) {
|
||||||
|
denyAnyDependency(
|
||||||
|
this.domainPackages, Collections.singletonList(adapters.basePackage), classes);
|
||||||
|
denyAnyDependency(
|
||||||
|
this.domainPackages, Collections.singletonList(applicationLayer.basePackage), classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void check(JavaClasses classes) {
|
||||||
|
this.adapters.doesNotContainEmptyPackages();
|
||||||
|
this.adapters.dontDependOnEachOther(classes);
|
||||||
|
this.adapters.doesNotDependOn(this.configurationPackage, classes);
|
||||||
|
this.applicationLayer.doesNotContainEmptyPackages();
|
||||||
|
this.applicationLayer.doesNotDependOn(this.adapters.getBasePackage(), classes);
|
||||||
|
this.applicationLayer.doesNotDependOn(this.configurationPackage, classes);
|
||||||
|
this.applicationLayer.incomingAndOutgoingPortsDoNotDependOnEachOther(classes);
|
||||||
|
this.domainDoesNotDependOnOtherPackages(classes);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user