前回の記事 レイヤー間の依存関係の静的解析 - PHP deptrac ~ 導入編 からの続きです。
「ユニットテストとは事情が違うし、そうそう違反は起きないよ」と思った方がいらっしゃるかも知れません。いえ、レイヤーの依存関係違反は割と発生します。
起こりうる違反例
前回のコードを少し変更して、Userエンティテイにおいて getType()
メソッドで属性を取得できるようにしてみましょう。
<?php namespace Foo\Entity; class User { public function getType() : string { // 何かしらのロジック return \Foo\Repository\UserRepository::TYPE_ADMIN; } }
<?php namespace Foo\Repository; use Foo\Entity\User; class UserRepository { public const TYPE_ADMIN = 'admin'; public function findOneById(int $id) : User {} }
Entityまでのレイヤールールを行うと・・・・
paths: - ./src exclude_files: layers: - name: Action collectors: - type: implements implements: Psr\Http\Server\RequestHandlerInterface - name: Repository collectors: - type: className regex: Foo\\Repository\\.*Repository - name: Entity collectors: - type: className regex: Foo\\Entity\\.* ruleset: Action: - Repository - Entity Repository: - Entity
$ deptrac analyse action-repository-entity.yaml 8/8 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% ----------- ------------------------------------------------------------------------------- Reason Entity ----------- ------------------------------------------------------------------------------- Violation Foo\Entity\User must not depend on Foo\Repository\UserRepository (Repository) /mnt/d/dev/sandbox-deptrac/src/Entity/User.php:10 ----------- ------------------------------------------------------------------------------- -------------------- ----- Report -------------------- ----- Violations 1 Skipped violations 0 Uncovered 6 Allowed 5 Warnings 0 Errors 0 -------------------- -----
と、Entityは他に参照するレイヤーはあるべきでないのに、UserRepositoryの定数を利用していたことが判明します。 実際のプロジェクトでも数は少なくはありながらも、「サービスクラスの定数を Entityクラス内で用いてしまっていた。」などがありました。
ほかの違反例
さきほどの例では、レイヤー外の定数を挙げましたが、ほかには以下のような違反を発見しました。
- Entityでのメソッドで、サービスレイヤーとして定義した層の例外クラスの使用
- これは、定数の例と同様にサービスクラス側に設けた、サービスの例外をエンティテイの例外に利用してしまったというミスです。
現行のエラーをひとまずレポートしないようにする。~ baselineの利用
実際に、既存のプロジェクトに対してレイヤールールをある程度行い、analyseを実行してみると上記で述べたような違反が検出されてくることになります。その場合、deptrac を導入したくても既存の違反分の修正を行わなくては、と思われるかも知れません。ご安心ください。deptracには、phpstanやpsalmのものと同様に既存の違反分を許容するベースラインサポートがあります。
baselineの作成
--formatter=baseline
でbaselineを作成します。
$ deptrac analyse --formatter=baseline --baseline-dump=baseline.yaml action-repository-entity.yaml 8/8 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% Baseline dumped to /mnt/d/dev/sandbox-deptrac/baseline.yaml
以下のような既存の違反に対して skip_violationsが作成されます。
$ cat baseline.yaml skip_violations: Foo\Entity\User: - Foo\Repository\UserRepository
baselineのインクルード
baseline:
行を追加します。
paths: - ./src exclude_files: baseline: baseline.yaml
baselineを含んだ上での再実行
baselineを含ませて改めて実行してみます。
$ deptrac analyse action-repository-entity.yaml 8/8 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% -------------------- ----- Report -------------------- ----- Violations 0 Skipped violations 1 Uncovered 6 Allowed 5 Warnings 0 Errors 0 -------------------- -----
Skipped violations の対象となり、exitコードも0です。
Github Actions でのアノテート
CIでの運用の場合、deptracはさらにフォーマッターとしてgithub-actions
が用意されています。
弊社では、deptrac analyse action-repository-entity.yaml --no-progress --formatter=github-actions
といったワークフローを設定しています。
なお、補足として、0.11.1以降ではGithub Actionsのフォーマッターでのスキップ対応 を含めスキップ分のレポートはデフォルトでは除外されるようになり、Github Actionsでのアノテートなどでの導入が行いやすくなりました。
継続的なアーキテクチャの維持・向上に向けて
上述のbaselineのサポートもあり、もし既存のコードに対してアーキテクチャを失敗していた!ということが見つかっても、恐れることなく即レイヤー設定の追加・アップデートが行えるようになりました。自身が担当した改修部分に限らず、コードレビューにおいて見つかった点をフィードバックする形で deptrac.yaml に追加し継続的にアーキテクチャテストを行っていきたいと思います。