0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

アーキテクチャテストAdvent Calendar 2020

Day 23

ArchUnit 実践:Onion Architecture のアーキテクチャテスト

Last updated at Posted at 2020-12-23
// 実行環境
* AdoptOpenJDK 11.0.9.1+1
* JUnit 5.7.0
* ArchUnit 0.14.1

レイヤーの依存関係

  • 依存の方向は外側の層から内側の層への一方通行
  • 一番外側のアダプター同士は依存しない

image.png

Java プロジェクトのパッケージ構成

image.png

アーキテクチャテストの実装

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
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?