ドメインオブジェクトを中心としたClean Architectureは、どういうレイヤー構成にするとよいか、簡単にまとめてみた。
イメージ
たぶん、こんな感じになるはず。通常は円状に表現するが、わかりにくいので層状に書いてみた。
レイヤー構成
赤い部分の層は、直接依存の方向が上から下です。グレー部分の層は、契約だけが定義された独立した層で、ユースケース層やインターフェイス層から依存できるものとします。
- インターフェイス(アダプタ)層
- 内外とのデータ形式の変換が主な役割
- コントローラ、プレゼンター(内部から外部へデータ形式を変換する責務),ゲートウェイ(外部と通信する責務。DBやRPC)
- ユースケース層
- アプリーケーション層ともいう
- アプリケーション固有のビジネスルールをカプセル化する
- ドメイン層
- Clean Architecture本では、中心にはエンティティとだけ書かれているが、DDDでは中心はドメイン層になる
- ドメインモデルを表現した実装
- 契約層(契約だけを表現した型を配置)
- ⚠️こういう層を定義するというのはClean Architectureにはない、アイデアです。
- Input Port, Output Port, DTO
- リポジトリ
- DAO, Record
DIによるオブジェクトグラフの構築
- コントローラに対してDIするオブジェクト
- ユースケース
- プレゼンター
- ユースケースに対してDIするオブジェクト
- リポジトリ
- DAO(CQRSのリード責務なら)
インフラストラクチャ層はどこに行った?
インフラストラクチャ層は、要件が変化しても依存し続ける層。すべての層が依存できる汎用的な技術基盤(言語を拡張するような実装が提供されることが多い)。Clean Architectureでも導入できる。インターフェイス層とごっちゃにしないこと。
所感
- 永続化に関する層が上位に配置される以外は、伝統的なレイヤー化アーキテクチャと基本的に差がないと考えてよい。あとは、Input Port, Output Portが仰々しく見えるだけ。
- Clean Architecture本によると、Input PortとOutput Portはバウンダリの抽象っぽい。バウンダリだけに限らず、永続化に関するInput Port, Output Portの概念があってもよい気がするが、用語の使い方はClean Architecture本に合わせておいた。
- インターフェイス層からユースケースの実装を直接呼べてしまうので、Input Portの抽象がどこまで必要かわからない。そもそも抽象がなくてもいいのではないか。Output Portは間接依存のために必要。コールバックとして抽象化する方法もあるし、単にプレゼンターのインターフェイスだけを定義する方法もある。
- 集約は本来ライフサイクルのためのパターンの一つなので、インターフェイス層ではないか説がある。集約にも契約と実装に分離し、実装はインターフェイス層に配置した方がいいかもしれない。