バニラ PHP で学ぶクリーンアーキテクチャ その 1

クリーンアーキテクチャの概要を PHP のサンプルコードを見ながら学びます。

対象

  • クリーンアーキテクチャの記事や本を見たけど腑に落ちなかった
  • クリーンアーキテクチャの概念は分かったが実際にどう実装していくか、動くコードを見たい

基本的には素の PHP ですが、 ある程度モダンな環境にしたかったので Composer, PHPUnit, PHP-CS-Fixer, PHPStan は入れてます。 大規模なフレームワークを使ってないくらいに考えてください。

題材

例として、商品情報管理ツールを作ります。

  1. 実行すると、指定された商品情報に税込価格を追加して表示する
    • 税込価格は軽減税率を考慮して算出する
  2. コマンドラインツールとして作る。商品 ID を引数で指定する
  3. 商品情報は LTSV 形式で標準出力に表示する
  4. 商品情報として以下の項目がストレージに保存されている
    • 商品 ID
    • 商品名
    • 税抜価格
    • 軽減税率対象か
  5. ストレージはメモリ上に保存される

リポジトリここ にあります。 記事を読みながら見るとよいです。

クリーンアーキテクチャのレイヤ

クリーンアーキテクチャといえば、 元記事の図 が有名ですが、ここで 以下のレイヤが定義されています。

  • Frameworks & Drivers
  • Interface Adapters
  • Application Business Rules
  • Enterprise Business Rules

それぞれについて説明します。

Frameworks & Drivers

  • html を生成するテンプレートエンジン
  • DB にアクセスするライブラリ
  • web フレームワーク

などの具体的な処理をおこないうものが含まれます。 ソースでは src/Infrastructure/ が対応します。 今回は引数の処理とストレージの実装をおこなっています。

Interface Adapters

外部との仲介をおこないます。

  • リクエストパラメータのバリデーション、正規化
  • ロジックの呼び出し
  • 出力のフォーマット
  • ストレージからデータを取得し、Entity に変換

などを担当します。 ソースでは src/Adapter/ が対応します。

今回は

をおこなっています。

Application Business Rules

アプリケーション固有のビジネスロジックを実装します。

ソースでは src/Application/ が対応します。

今回は以下をおこなっています。

  • ストレージへのアクセス
  • ドメインロジックの呼び出し
  • エラー時の制御

Enterprise Business Rules

アプリケーションによらないビジネスロジックを実装します。 分かりにくいですが、私の解釈では 「アプリケーションがなくても存在するようなロジック」 をここに書きます。 今回の場合だと「消費税率の計算」は アプリがなくても存在する(法律で規定されている) ため、ここに記述しています。

ソースでは src/Domain/ が対応します。

今回は税込価格の計算ロジックを実装しています。

依存の向きと依存性逆転の原則について

方針 (Policy) を詳細 (Detail) に依存させないために、 依存性逆転の原則 (DIP) を使います。

各レイヤは Detail -> Policy の順に

Frameworks & Drivers -> Interface Adapters -> Application Business Rules -> Enterprise Business Rules

となっており、この逆の依存はさせないようにします。 元記事 の図でいうと、外 -> 中は OK ですが、中 -> 外への依存は NG になります。

とはいえ、ビジネスロジックから DB 等にアクセスする事はあるので、 その際は Interface を定義し、それに依存するように実装します。

今回の場合 ItemInteractor (ビジネスロジック)から ItemRepository (DB 等にアクセス) を使う必要がありますが、 直接は参照できないため、IItemRepository インタフェースを定義して ItemRepository には依存させないようにしています。

実装でも ItemInteractorIItemRepository インタフェースのみを参照しています。 また、ソースを 'use' で検索しても外 -> 中の参照しかないことが分かります。

このようにすることで、例えばストレージが DB から Web API に変わったとしても、 内部(Application, Domain) への影響をおさえる事ができます。

まとめ

という事で、今回は各レイヤの説明をしました。 次回、ソースコードの説明をします。

サンプルの動かし方

実際に動かしてみたり、変更してみたりするとよいかもです。 PHP 7.4.9 で動作確認しています。

$ git clone https://github.com/yosugi/clearn-architecture-with-vanilla-php.git 
$ cd clearn-architecture-with-vanilla-php
$ composer install
$ php ./src/main.php 1
id:1 name:itemA price:100 isReducedTaxRate:false priceIncludingTax:110
$ php ./src/main.php 99
item id 99 is not found.