Clean Architecture は一つの選択肢でしかない
PofEAA によれば、エンタープライズアプリケーションアーキテクチャにおけるドメインロジックの実装方式は以下の3つに大別できます。
さらに、Domain Model については以下の2つに大別できます。
Active Record は Rails に代表される、Domain Object でデータアクセスをラッピングするパターン。
Data Mapper は各種 ORM に代表される、Domain Object とデータアクセスをマッピングするパターン。
この中で Clean Architecture に則ることのできるものは、Data Mapper による Domain Model のみです。
それ以外は中心レイヤー (依存関係の行き着く先) にデータベースやフレームワークが居座ってしまいます。
何が言いたいかというと、「全てのアーキテクチャは Clean Architecture に則っていなければいけない、なんてことは無い」ということです。Clean Architecture 以外のアーキテクチャも、れっきとした選択肢として存在しています。
ただ、Clean Architecture が最も綺麗で洗練されたアーキテクチャであることも確かです。条件 (プロジェクトの規模や全体のスキルレベルなどなど) が揃えば採用すべきです。
結局はトレードオフ
Clean Architecture | DDD |
---|---|
Enterprise Business Rules | Domain Layer |
Application Business Rules | Application Layer |
Interface Adapters | Presentation Layer, Infrastructure Layer |
Frameworks & Drivers | Presentation Layer 及び Infrastructure Layer とのグルーコード |
DDD のレイヤー構造をとれば Clean Architecture になるということではありません。 Domain Layer や Application Layer はまぁ Clean Architecture になっていると思いますが、Presentatoin Layer や Infrastructure Layer は意識的に目指さなければ Clean Architecture にはなりません。Presentation Layer は Web Framework に、Infrastructure Layer は ORM Framework にべったりになりがちだからです。Clean Architecture はこれらのフレームワークへの依存を、プロキシを挟んで (Humble Object に追いやって) 外側に隔離しろと言っています。2
ただ、結局はコストと恩恵3のトレードオフです。フレームワークを無条件に隔離するではなく、或いはフレームワークに無条件に依存するではなく、慎重に検討する (決定を遅延する) ことが重要と思います。
ちなみに、フレームワークへの依存を除去しない場合を表現すると下記のようになります。
Clean Architecture | DDD |
---|---|
Enterprise Business Rules | Domain Layer |
Application Business Rules | Application Layer |
Frameworks & Drivers | Presentation Layer, Infrastructure Layer |
或いはこう。
Clean Architecture | DDD |
---|---|
Enterprise Business Rules | Domain Layer |
Application Business Rules | Application Layer |
Interface Adapters | Presentation Layer |
Frameworks & Drivers | Presentation Layer とのグルーコード, Infrastructure Layer |
Clean Architecture の図の右下部分 (と24章) はしっかり理解する
Clean Architecture の図の右下が示しているのは境界の越え方 (の一例) です。
この越え方をすれば、隣接する二つのレイヤーは双方向で分離される、つまりお互い独立してコンパイル・デプロイが可能になります。
要は DIP (依存関係逆転の原則) です。ただし、本来 DIP は提供インターフェイス4 (ここでは Use Case Input Port) だけ抽出すれば実現できますが、要求インターフェイス5 (ここでは Use Case Output Port) も登場させることで内側から外側のフローを制御できるようになり、裏ルート (内側から外側への直接の依存) が徹底的に予防されています。
24章で語られている通り境界の越え方は他にもあります。簡単に表にまとめてみます。
分離 | 提供インターフェイス | 要求インターフェイス | 抽象コンポーネント | DIP |
---|---|---|---|---|
双方向 (完全) | ○ | ○ | ○ | ○ |
双方向 (部分) | ○ | ○ | × | × |
片方向 (Strategy) | ○ | × | ○ | ○ |
片方向 (Facade) | × | × | × | × |
この双方向と片方向が非常にわかりづらいですね。Strategy も DIP なのだから双方向の分離ができているように思いますが、要求インターフェイスが無いため裏ルートの予防ができていないということで、片方向止まりとなるわけです。
と、話が少し逸れてしまいましたが、やはり大事なのは境界の越え方について理解し慎重に検討することです。
Controllers や Presenters を用意することや右下の図を無条件に踏襲することが大事なのではありません。これらはあくまで手段です。
YAGNI についても言及されていますが、YAGNI で乗り越えられると判断するのであればそれでも良いと思います。(プロジェクトによると思います。)
-
Infrastructure が Presentation まで支えることはあまり無いかもですが。
これを Clean Architecture に当てはめると、以下のようになります。 ↩ -
更に ORM については、『ドメインオブジェクトと DB を直接マッピングするのではなく、データ構造を間に挟んで間接的にマッピングしろ』と、23章で暗に示唆しているように見えます。ただ、そうすると Layzy Load や Unit of Work が機能しなくなるので非現実的です。 ↩
-
Interface Adapters のフレームワーク依存の除去は、Enterprise Business Rules や Application Business Rules ほど重要ではありません。 ↩
-
クライアントに提供するインターフェイス。クライアントが直接呼び出す。 ↩
-
クライアントに要求するインターフェイス。クライアントが実装し呼び出される。
なお、(わかりづらいですが) DIP ですのでインターフェイス (及び付随するデータ構造) は抽象コンポーネントにまとめられ、Use Case Interactor 含む具象コンポーネントからは切り離されます。
↩