1. 導入:なぜ設計のルールを「テスト」するのか
開発が進むにつれて、「Controllerから直接データベースを操作してはいけない」「特定のパッケージのクラスは外部から参照してはいけない」といったルールが守られなくなり、コードがスパゲッティ化してしまうことはありませんか?
人間によるコードレビューには限界があります。そこで登場するのがArchUnitです。ArchUnitは、アーキテクチャの設計ルールを「単体テスト」として記述することで、ルール違反があった場合にビルドを失敗させ、設計の腐敗を未然に防ぐ強力なツールです。
2. 基礎知識:ArchUnitとは何か
ArchUnitは、Java(や.NET)のコードを静的解析し、クラス間の依存関係や命名規則をプログラムコードとして検証するライブラリです。
通常、単体テストは「メソッドの出力値」を検証しますが、ArchUnitは「クラスがどこに配置されているか」「どのクラスを呼び出しているか」というアーキテクチャそのものを検証します。これにより、チームの設計指針を「ドキュメント」ではなく「実行可能なコード」として強制できます。
3. 実装/解決策:テストコードの書き方
ArchUnitの導入は簡単です。JUnitのテストクラスとして記述するだけで、ビルドパイプラインの一部として組み込めます。以下の手順でルールを定義します。
1. 検証対象のクラス群を読み込む(JavaClasses)。
2. ルールを定義する(例:ControllerはServiceのみに依存すべき)。
3. checkメソッドでルールを検証する。
4. サンプルプログラム:依存関係を制限するテスト例
JUnit 5とArchUnitを使用した、ControllerからService以外への直接アクセスを禁止するサンプルです。
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.ArchRule;
import org.junit.jupiter.api.Test;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
public class ArchitectureTest {
@Test
void controllerはservice層以外に依存してはいけない() {
// 1. プロジェクトのクラスをすべて読み込む
JavaClasses importedClasses = new ClassFileImporter().importPackages(“com.example.myapp”);
// 2. ルールの定義:Controllerという名前のクラスは、..service..パッケージ以外に依存しない
ArchRule rule = classes()
.that().haveSimpleNameEndingWith(“Controller”)
.should().onlyDependOnClassesThat().resideInAnyPackage(“..service..”, “java..”, “javax..”);
// 3. ルールを検証する
rule.check(importedClasses);
}
}
5. 応用・注意点:現場で役立つ運用のコツ
陥りやすい罠:厳しすぎるルール
最初から全ての制限をかけると、既存コードの修正で大量のテストエラーが発生し、開発が止まってしまいます。まずは「循環参照の禁止」や「命名規則の統一」など、緩やかなルールから導入しましょう。
補足:設定のコツ
- 除外設定: テストコード自体や、ライブラリのクラスを解析対象から外す設定も可能です。
- CI/CDへの統合: ビルド時に必ず実行されるように設定しましょう。GitHub Actionsなどで実行すれば、ルール違反のコードをマージする前に検知できます。
ArchUnitを使えば、チームの設計思想を自動的に守り続けることができます。ぜひ、あなたのプロジェクトでも「設計の保護」を自動化してみてください。

コメント