// 実行環境
* AdoptOpenJDK 11.0.9.1+1
* Spring Boot 2.4.0
* JUnit 5.7.0
* ArchUnit 0.14.1
アーキテクチャテストのモチベーション
ドメイン層は技術的な詳細に依らず、ビジネスのコアロジックを表現します。それらは特定の実行環境やフレームワークとも疎であるべきです。
アーキテクチャテストの実装
ドメイン層は Web 実行環境 (Java Servlet) に依存しない
package com.example;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import org.junit.jupiter.api.Test;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
class ArchitectureTest {
// 検査対象のクラス
private static final JavaClasses CLASSES =
new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("com.example");
@Test
void ドメイン層はWeb実行環境に依存しない() {
noClasses().that().resideInAPackage("com.example.domain..")
.should()
.dependOnClassesThat().resideInAPackage("javax.servlet..")
.check(CLASSES);
}
}
ドメイン層は Web アプリケーションフレームワーク (Spring Framework) に依存しない
package com.example;
import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import org.junit.jupiter.api.Test;
import org.springframework.transaction.annotation.Transactional;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
class ArchitectureTest {
// 検査対象のクラス
private static final JavaClasses CLASSES =
new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("com.example");
@Test
void ドメイン層はWebアプリケーションフレームワークに依存しない() {
noClasses().that().resideInAPackage("com.example.domain..")
.should()
.dependOnClassesThat(new DescribedPredicate<>("are part of the Spring Framework") {
/**
* @param clazz 検査対象のクラスが依存するクラス
* @return 検査対象のクラスが依存するクラスが Spring Framework のクラスである場合、true
*/
@Override
public boolean apply(final JavaClass clazz) {
return clazz.getPackageName().startsWith("org.springframework")
// 特定のアノテーションへの依存は許可
&& ! clazz.isEquivalentTo(Transactional.class);
}
})
.check(CLASSES);
}
}