こんにちは。エンジニアの濱田 (@hamakou108) です。
PHP 開発者の皆さん、 Deptrac を活用していますか?
私たちのプロダクトでは、アーキテクチャの整合性を保つために Deptrac を利用して依存関係のチェックを行っています。プロダクトの成長に伴ってアーキテクチャが複雑になるにつれ、 Deptrac の設定ファイルも肥大化しやすくなり、メンテナンス性の低下を招くことがあります。そこで今回、設定ファイルの構成を見直すことで、見通しのよい構成へと改善し、メンテナンス性の低下を抑えることができました。本記事では、その取り組みについて紹介します。
Deptrac とは?
Deptrac は、 PHP コードベースにおけるレイヤー間の依存関係を定義し、ルールに基づいて検証する静的解析ツールです。
よくある4層レイヤードアーキテクチャを例に挙げて説明します。4層レイヤードアーキテクチャは、プレゼンテーション層、アプリケーション層、ドメイン層、インフラ層の4つのレイヤーに分かれており、ドメイン層に向かって外側から内側のレイヤーへと依存するような構造を持ちます。
このアーキテクチャの依存関係を Deptrac で表現する場合、たとえば次のようになります。
deptrac: layers: - name: Presentation collectors: - type: directory value: './src/Presentation/.*' - name: Application collectors: - type: directory value: './src/Application/.*' - name: Domain collectors: - type: directory value: './src/Domain/.*' - name: Infrastructure collectors: - type: directory value: './src/Infrastructure/.*' ruleset: Presentation: - Application Application: - Domain Infrastructure: - Domain
layers
はコードベースを論理的なレイヤーに分割するための設定で、それぞれのレイヤーがどのディレクトリに対応するかは collectors
で指定されます。たとえば、 Presentation
レイヤーは src/Presentation/
以下のファイルを対象とします。
ruleset
はレイヤー間の依存関係ルールを定義しており、どのレイヤーがどのレイヤーに依存してよいかを示します。この例では、 Presentation
から Application
、 Application
から Domain
、 Infrastructure
から Domain
への依存が許可されており、これ以外のレイヤー間の依存は禁止されます。このようにしてアーキテクチャの意図と異なるような依存関係が発生してしないか検証することができます。
Deptrac の設定の肥大化
私たちのプロダクトでは、これまで4層のレイヤードアーキテクチャを採用してきました。しかし、プロダクトの成長に伴い、このアーキテクチャだけではコードベースを効果的に管理することが難しくなってきました。そこで、同じコードベース内でサービスごとにモジュールを分割する、いわゆるモジュラーモノリスのアーキテクチャも併せて採用することにしました。
せっかく Deptrac を使っているので、レイヤードアーキテクチャの依存関係に加えて、サービス境界を跨いだ依存関係が発生していないことも検証してみようと思います。 Deptrac では1つのクラスが複数のレイヤーに含まれると警告として検知されてしまいます。このため、そのようなクラスが発生しないように注意しつつ、先ほどの Deptrac の設定ファイルを変更してみます。
# Foo と Bar という2つのサービスが # トップレベルで分割されている状況を想定 deptrac: layers: - name: FooPresentation collectors: - type: directory value: './src/Foo/Presentation/.*' - name: FooApplication collectors: - type: directory value: './src/Foo/Application/.*' - name: FooDomain collectors: - type: directory value: './src/Foo/Domain/.*' - name: FooInfrastructure collectors: - type: directory value: './src/Foo/Infrastructure/.*' - name: BarPresentation collectors: - type: directory value: './src/Bar/Presentation/.*' - name: BarApplication collectors: - type: directory value: './src/Bar/Application/.*' - name: BarDomain collectors: - type: directory value: './src/Bar/Domain/.*' - name: BarInfrastructure collectors: - type: directory value: './src/Bar/Infrastructure/.*' ruleset: FooPresentation: - FooApplication FooApplication: - FooDomain FooInfrastructure: - FooDomain BarPresentation: - BarApplication BarApplication: - BarDomain BarInfrastructure: - BarDomain
少し見通しが悪くなってきました。同じような言葉が複数回出てくるようになり、記述が冗長に見えます。
より現実的な状況を考えると、レイヤーを定義するための collectors
の条件が複雑になったり、より細かな単位で依存関係をチェックする必要があったりと、さまざまな要請から設定ファイルが肥大化する可能性があります。このまま拡張を続けると、 Deptrac の設定のメンテナンス性は徐々に低下していくでしょう。
関心事による Deptrac 設定の分割
この問題に対してどのように取り組めば良いでしょうか。実は Deptrac のドキュメント中にその指針が示されています。
Typically software has more than just one view. It is possible to use multiple config files, to take care about different architectural views.
一般的にソフトウェアにはさまざまなビューが存在します。それぞれのアーキテクチャビューに対応するために、複数の設定ファイルを使い分けることができます。
https://deptrac.github.io/deptrac/concepts/#different-layers-and-different-views
つまり、アーキテクチャの関心事に沿って設定ファイルを分解することで、見通しのよい設定にすることができます。
この指針に従って、前述の例の設定ファイルを分割してみましょう。各ファイルには、それぞれのアーキテクチャの関心事に関連するレイヤーとルールのみを記述します。
deptrac.layered-architecture.yaml
deptrac: layers: - name: Presentation collectors: - type: directory value: './src/.*/Presentation/.*' - name: Application collectors: - type: directory value: './src/.*/Application/.*' - name: Domain collectors: - type: directory value: './src/.*/Domain/.*' - name: Infrastructure collectors: - type: directory value: './src/.*/Infrastructure/.*' ruleset: Presentation: - Application Application: - Domain Infrastructure: - Domain
deptrac.bounded-context.yaml
parameters: paths: - ./src layers: - name: Foo collectors: - type: directory value: './src/Foo/.*' - name: Bar collectors: - type: directory value: './src/Bar/.*' # レイヤー間の依存関係は許可しないため、 ruleset の定義は不要
deptrac.layered-architecture.yaml
は記事の序盤で紹介した設定ファイルとほぼ同じですが、各レイヤーは Foo と Bar のそれぞれサービス用ディレクトリに存在するため、 collectors
で指定するパスを正規表現に変更しています。 deptrac.bounded-context.yaml
は単純に各サービスに対応するレイヤーを定義するだけで済むようになりました。
チェックを実行する際は、分割された設定ファイルそれぞれについて deptrac
コマンドを実行する必要があります。
./vendor/bin/deptrac analyse --config-file=deptrac.layered-architecture.yaml ./vendor/bin/deptrac analyse --config-file=deptrac.bounded-context.yaml
設定ファイル数の増加に伴ってチェックの総実行時間も増加しますが、 Deptrac はキャッシュ機能を備えているため、私たちの環境ではそれほど増えることはありませんでした。
まとめ
プロダクトの成長に伴いアーキテクチャの構成が増えていく中で、 Deptrac の設定ファイルを関心事ごとに分割することで、メンテナンス性の低下を抑える取り組みについて紹介しました。 Deptrac を活用している皆さんも、設定ファイルの構成を見直すことで、より効果的にアーキテクチャを管理できる可能性があります。ぜひ一度、設定の見直しを検討してみてはいかがでしょうか。