// 実行環境
* AdoptOpenJDK 11.0.9.1+1
* JUnit 5.7.0
* ArchUnit 0.14.1
レイヤーの依存関係
- 依存の方向は外側の層から内側の層への一方通行
- 一番外側のアダプター同士は依存しない
Java プロジェクトのパッケージ構成
アーキテクチャテストの実装
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.library.Architectures.onionArchitecture;
class ArchitectureTest {
// 検査対象のクラス
private static final JavaClasses CLASSES =
new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("com.example");
@Test
void オニオンアーキテクチャのアーキテクチャテスト() {
onionArchitecture()
// domain.model パッケージをドメインモデル層として定義
.domainModels("com.example.domain.model..")
// domain.service パッケージをドメインサービス層として定義
.domainServices("com.example.domain.service..")
// application パッケージをアプリケーションサービス層として定義
.applicationServices("com.example.application..")
// infrastructure パッケージをインフラストラクチャ・アダプターとして定義
.adapter("infra", "com.example.infrastructure..")
// presentation パッケージをユーザインターフェイス・アダプターとして定義
.adapter("ui", "com.example.presentation..")
.check(CLASSES);
}
}
アーキテクチャテストの実行例
テスト失敗例①(ドメインサービス→アプリケーションサービスへの依存)
内側のドメインサービス層の Service クラスが、外側のアプリケーションサービス層の UseCase クラスに依存してしまっている、というアーキテクチャ違反を検知した想定でのテスト失敗例。
$ ./gradlew clean check
> Task :test FAILED
ArchitectureTest > オニオンアーキテクチャのアーキテクチャテスト() FAILED
java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'Onion architecture consisting of
domain models ('com.example.domain.model..')
domain services ('com.example.domain.service..')
application services ('com.example.application..')
adapter 'infra' ('com.example.infrastructure..')
adapter 'ui' ('com.example.presentation..')' was violated (2 times):
Constructor <com.example.domain.service.employee.EmployeeRegisterService.<init>(com.example.application.HogeUseCase)> has parameter of type <com.example.application.HogeUseCase> in (EmployeeRegisterService.java:0)
Field <com.example.domain.service.employee.EmployeeRegisterService.hogeUseCase> has type <com.example.application.HogeUseCase> in (EmployeeRegisterService.java:0)
at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:94)
at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:82)
at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:267)
at com.tngtech.archunit.library.Architectures$OnionArchitecture.check(Architectures.java:538)
at com.example.ArchitectureTest.オニオンアーキテクチャのアーキテクチャテスト(ArchitectureTest.java:560)
1 test completed, 1 failed
テスト失敗例②(ユーザインターフェイス・アダプター→インフラストラクチャ・アダプターへの依存)
ユーザインターフェイス・アダプターの Controller クラスが、インフラストラクチャ・アダプターの Repository(Impl) クラスに依存してしまっている、というアーキテクチャ違反を検知した想定でのテスト失敗例。
$ ./gradlew clean check
> Task :test FAILED
ArchitectureTest > オニオンアーキテクチャのアーキテクチャテスト() FAILED
java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'Onion architecture consisting of
domain models ('com.example.domain.model..')
domain services ('com.example.domain.service..')
application services ('com.example.application..')
adapter 'infra' ('com.example.infrastructure..')
adapter 'ui' ('com.example.presentation..')' was violated (2 times):
Constructor <com.example.presentation.employee.EmployeeController.<init>(com.example.infrastructure.datasource.EmployeeRepositoryImpl)> has parameter of type <com.example.infrastructure.datasource.EmployeeRepositoryImpl> in (EmployeeController.java:0)
Field <com.example.presentation.employee.EmployeeController.employeeRepository> has type <com.example.infrastructure.datasource.EmployeeRepositoryImpl> in (EmployeeController.java:0)
at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:94)
at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:82)
at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:267)
at com.tngtech.archunit.library.Architectures$OnionArchitecture.check(Architectures.java:538)
at com.example.ArchitectureTest.オニオンアーキテクチャのアーキテクチャテスト(ArchitectureTest.java:560)
1 test completed, 1 failed