ソフトウェアを構築する場合、開発チームは通常、ベストプラクティスと見なされるコーディングの一連のガイドラインと規則を定義します。
これらは通常、文書化され、それらを採用した開発チーム全体に伝達される方法です。ただし、開発中に、開発者はこれらのガイドラインに違反する可能性があります。これらのガイドラインは、コードレビュー中またはコード品質チェッカーを通じて見つかります。
したがって、重要な側面は、チェックを最適化するために、プロジェクトアーキテクチャ全体でこれらのディレクティブを可能な限り自動化することです。
これらのガイドラインは、ArchUnitを使用して検証可能なJUnitテストとして実装できます 。これにより、アーキテクチャ違反が発生した場合にソフトウェアバージョンのビルドが確実に停止されます。

ArchUnit は、単純なJavaユニットテスト環境で使用するJavaコードのアーキテクチャをテストするための、無料でシンプルで拡張可能なライブラリです。つまり、ArchUnitは、パッケージとクラス、レベルとスライスの間の依存関係をチェックしたり、循環依存関係をチェックしたりすることができます。これは、特定のJavaバイトコードを解析し、すべてのクラスをJavaコード構造にインポートすることによって行われます。
ArchUnit , :
ArchUnit JUnit 5, Maven Central:
pom.xml
XML
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<version>0.14.1</version>
<scope>test</scope>
</dependency>build.gradle
Groovy
dependencies {
testImplementation 'com.tngtech.archunit:archunit-junit5:0.14.1'
} }
Java
class ArchunitApplicationTests {
private JavaClasses importedClasses;
@BeforeEach
public void setup() {
importedClasses = new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("com.springboot.testing.archunit");
}
@Test
void servicesAndRepositoriesShouldNotDependOnWebLayer() {
noClasses()
.that().resideInAnyPackage("com.springboot.testing.archunit.service..")
.or().resideInAnyPackage("com.springboot.testing.archunit.repository..")
.should()
.dependOnClassesThat()
.resideInAnyPackage("com.springboot.testing.archunit.controller..")
.because("Services and repositories should not depend on web layer")
.check(importedClasses);
}
}-.
class ArchunitApplicationTests {
private JavaClasses importedClasses;
@BeforeEach
public void setup() {
importedClasses = new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("com.springboot.testing.archunit");
}
@Test
void serviceClassesShouldOnlyBeAccessedByController() {
classes()
.that().resideInAPackage("..service..")
.should().onlyBeAccessed().byAnyPackage("..service..", "..controller..")
.check(importedClasses);
}
}ArchUnit API-, DSL, , , . .
( AspectJ Pointcuts).
Java
class ArchunitApplicationTests {
private JavaClasses importedClasses;
@BeforeEach
public void setup() {
importedClasses = new ClassFileImporter()
importedClasses = new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("com.springboot.testing.archunit");
}
@Test
void serviceClassesShouldBeNamedXServiceOrXComponentOrXServiceImpl() {
classes()
.that().resideInAPackage("..service..")
.should().haveSimpleNameEndingWith("Service")
.orShould().haveSimpleNameEndingWith("ServiceImpl")
.orShould().haveSimpleNameEndingWith("Component")
.check(importedClasses);
}
@Test
void repositoryClassesShouldBeNamedXRepository() {
classes()
.that().resideInAPackage("..repository..")
.should().haveSimpleNameEndingWith("Repository")
.check(importedClasses);
}
@Test
void controllerClassesShouldBeNamedXController() {
classes()
.that().resideInAPackage("..controller..")
.should().haveSimpleNameEndingWith("Controller")
.check(importedClasses);
}
}
— . , Service, Component . .
Java
class ArchunitApplicationTests {
private JavaClasses importedClasses;
@BeforeEach
public void setup() {
importedClasses = new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("com.springboot.testing.archunit");
}
@Test
void fieldInjectionNotUseAutowiredAnnotation() {
noFields()
.should().beAnnotatedWith(Autowired.class)
.check(importedClasses);
}
@Test
void repositoryClassesShouldHaveSpringRepositoryAnnotation() {
classes()
.that().resideInAPackage("..repository..")
.should().beAnnotatedWith(Repository.class)
.check(importedClasses);
}
@Test
void serviceClassesShouldHaveSpringServiceAnnotation() {
classes()
.that().resideInAPackage("..service..")
.should().beAnnotatedWith(Service.class)
.check(importedClasses);
}
}API ArchUnit Lang Java. , , .
class ArchunitApplicationTests {
private JavaClasses importedClasses;
@BeforeEach
public void setup() {
importedClasses = new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("com.springboot.testing.archunit");
}
@Test
void layeredArchitectureShouldBeRespected() {
layeredArchitecture()
.layer("Controller").definedBy("..controller..")
.layer("Service").definedBy("..service..")
.layer("Repository").definedBy("..repository..")
.whereLayer("Controller").mayNotBeAccessedByAnyLayer()
.whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
.whereLayer("Repository").mayOnlyBeAccessedByLayers("Service")
.check(importedClasses);
}
}Spring Boot , .
ArchUnitは、階層化されたアーキテクチャが順守されているかどうかを確認するための一連の機能を提供します。これらのテストは、アクセスと使用が設定した制限内に維持されることを自動的に保証します。したがって、独自のルールを作成できます。この記事では、いくつかのルールについて説明しました。 ArchUnitの公式ドキュメントでは、さらに多くの可能性が紹介されています。
例の完全なソースコードは、私の GitHubリポジトリにあります。