Checkstyleでクリーンアーキテクチャをチェックする

2020年2月13日(木)15時7分 BIGLOBE Style

こんにちは、なおしむです。

私はシステム企画部でシステム全体のアーキテクトとレガシーシステムの改善開発をしています。

弊社ではドメイン駆動設計を使って開発をしています。 ドメイン駆動設計ではクリーンアーキテクチャのようなレイヤー構造でシステムを作ります。このレイヤー構造に従って設計・コーディングをするのですが、コードレビュー時に正しいレイヤー構造で作れているかをチェックするのが地味にめんどくさいです。。 現在のプロジェクトで、この地味で面倒なレイヤー構造のチェックをCheckstyleを使って自動化しているのでその方法を紹介します。

クリーンアーキテクチャのコードレビューはめんどくさい

クリーンアーキテクチャとは、図のような円形のレイヤー構造のアーキテクチャです。

The Clean Architectureを参考に筆者が作成

クリーンアーキテクチャにはレイヤー構造の依存関係にルールがあります。

この依存関係のルールでは、外側から内側への依存は許可しますが、内側から外側への依存は禁止です。コードレビューではこのルールに違反してないかをチェックします。具体的には各クラスのimport文を見ながらひとつひとつ確認します。たとえば、ドメイン層のクラスのレビューであればimport文にデータソース層がないかをチェックします。

このレビューが地味にめんどくさくてツライ。。

これをCheckstyleを使って自動化することが今回の本題です。

ドメイン層からデータソース層に依存している例

Checkstyleとは

Checkstyleはコードのコーディング規約違反をチェックするツールです。

本家サイトのoverviewを翻訳したものが下記です。

CheckstyleはプログラマーがJavaコードを書くための開発ツールです。コーディング標準に準拠するように支援してくれます。Javaコードをチェックする作業を自動化してくれ、コーディング標準を強制したいプロジェクトに有効です。

クリーンアーキテクチャのチェックもまさにコレ。

今回はCheckstyleを使ってimport文をチェックし、レイヤー構造の依存関係におけるルール違反を検知します。

Checkstyleの導入

checkstyle.xml配置

checkstyleディレクトリを作成しその中にファイルを作成します。

checkstyle/checkstyle.xml

<?xml version="1.0"?>
<!DOCTYPEmodule PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
<module name="TreeWalker">
<module name="ImportControl">

<!-- importチェックのファイルパスを指定する -->
<property name="file"value="checkstyle/import-control.xml"/>

</module>
</module>
</module>

checkstyle/import-control.xml

最終的にココに設定を追加していきます。

<?xml version="1.0"?>
<!DOCTYPEimport-control SYSTEM"checkstyle/import_control_1_4.dtd">
<!-- チェック対象のパッケージ -->
<import-control pkg="jp.co.biglobe">
</import-control>

checkstyle/import_control_1_4.dtd

これをDLして配置してください。


import_control_1_4.dtd

build.gradle

2行追加します。

buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'checkstyle'// ★追加
checkstyle.configFile = file('checkstyle/checkstyle.xml')// ★追加
...

この状態で./gradlew buildを実行して「許可されていないインポート」というエラーがたくさん出ればセットアップは完了です。

クリーンアーキテクチャの規約を設定

ここから先の設定はimport-control.xmlimport-controlタグ内に記述します。

今回のプロジェクトがこんなパッケージ構成になっている想定で説明します。

+ jp.co.biglobe.sample
+ api // API層: コントローラなど
+ datasource // データソース層: DBへのアクセスなど
+ service // サービス層
+ domain // ドメイン層

使って良いパッケージを設定

まず最初にプロジェクト内で使って良いパッケージを指定します。

クリーンアーキテクチャの話ではないですが、これをしないと全てエラーになってしまうので。。この設定はプロジェクトによって違うので適宜設定してください。

<!-- 使って良いライブラリ -->
<allow pkg="jp.co.biglobe.sample"/>
<allow pkg="lombok"/>
<allow pkg="org.springframework"/>
<allow pkg="java"/>
<allow pkg="javax"/>
<allow pkg="org.mybatis"/>
<allow pkg="org.apache.ibatis"/>

クリーンアーキテクチャの規約を設定

本丸です。

正規表現を使いながら設定します。

<!-- ドメイン層 -->
<!-- API層/データソース層/サービス層への依存禁止 -->
<subpackage name="domain">
<disallow class=".*\.api\..*"regex="true"/>
<disallow class=".*\.datasource\..*"regex="true"/>
<disallow class=".*\.service\..*"regex="true"/>
<disallow class="org.springframework.stereotype.Service"/><!-- domain層に@Serviceは禁止 -->
</subpackage>
<!-- サービス層 -->
<!-- API層/データソース層への依存禁止 -->
<subpackage name="service">
<disallow class=".*\.api\..*"regex="true"/>
<disallow class=".*\.datasource\..*"regex="true"/>
</subpackage>

ドメイン層では各層の依存に加えて@Serviceの利用も制限しています。

ドメインサービスとアプリケーションサービスを混同して使ってしまうことがあるので。

全体

import-control.xml全体としてはこんな感じです。

<?xml version="1.0"?>
<!DOCTYPEimport-control SYSTEM"checkstyle/import_control_1_4.dtd">
<import-control pkg="jp.co.biglobe.sample">
<!-- 使って良いライブラリ -->
<allow pkg="jp.co.biglobe.sample"/>
<allow pkg="lombok"/>
<allow pkg="org.springframework"/>
<allow pkg="java"/>
<allow pkg="javax"/>
<allow pkg="org.mybatis"/>
<allow pkg="org.apache.ibatis"/>
<!-- ドメイン層 -->
<subpackage name="domain">
<disallow class=".*\.api\..*"regex="true"/>
<disallow class=".*\.datasource\..*"regex="true"/>
<disallow class=".*\.service\..*"regex="true"/>
<disallow class="org.springframework.stereotype.Service"/><!-- domain層に@Serviceは禁止 -->
</subpackage>
<!-- サービス層 -->
<subpackage name="service">
<disallow class=".*\.api\..*"regex="true"/>
<disallow class=".*\.datasource\..*"regex="true"/>
</subpackage>
</import-control>

以上設定完了!

実行してみる

設定が終わったので./gradlew buildを実行してみます。

お!見事にドメイン層からデータソース層への依存を検知してくれました。

まとめ

今回はCheckstyleを使って、クリーンアーキテクチャのレイヤー構造に従っているかを自動検知する仕組みを作りました。

これで面倒なコードレビューから解放される〜。

それではまたー。

BIGLOBE Style

「システム」をもっと詳しく

「システム」のニュース

「システム」のニュース

トピックス

x
BIGLOBE
トップへ