オブジェクト指向Advent Calendarが空いていたので、せっかくなので投稿します。
概要
先日レイヤー化アーキテクチャとDIPについて共有することがあったのでその内容の焼き直しです。
内容自体は実践ドメイン駆動設計の4章アーキテクチャの前半を自分なりに解釈したものです。
レイヤー化アーキテクチャ
レイヤー化アーキテクチャはシステムの関心ごとをレイヤーごとに分けて疎結合にするためのアーキテクチャです。例を以下に示します。
レイヤー化アーキテクチャの上位のモジュールは下位のモジュールに依存することが許されています。また、図には書いてませんが上位、下位の関係が守れていればアプリケーション層からインフラストラクチャ層への依存といった層を跨いだ依存も許されます。逆に下位のモジュールから上位のモジュールへの依存は許されていません。
課題
レイヤー化アーキテクチャを素直に実装した例を下記に示します。
簡単に説明すると、idで指定したユーザーをactivateするというユースケースがあり、アプリケーション層でドメイン層とインフラストラクチャ層のオブジェクトを利用して、ユーザーを取得してactivateし保存する、という処理になります。
ここで問題となるのはインフラストラクチャ層のクラスがドメイン層のクラスに依存しているということです。レイヤー化アーキテクチャでは下位のモジュールは上位のモジュールに依存してはいけないので何とかしてこれを解決する必要があります。
Dependency Inversion Principle
上記の問題はSOLID原則のひとつであるDependency Inversion Principle(DIP)で解決できるとされています。DIPは下記の2つの主張をしています。
- 上位のモジュールが下位のモジュールに依存するのではなく、両者とも抽象に依存すべきである。
- 抽象は詳細に依存するべきではなく、詳細は抽象に依存すべきである。
DIPに従い問題となっていた部分を抽象に依存させた図が下記となります。
UserRepositoryのinterfaceをドメイン層に定義し、インフラストラクチャ層で実装することで、レイヤー化アーキテクチャの上位から下位への依存という条件を満たすことができました。
利点
- 依存が一方向になっているため、レイヤー化アーキテクチャの目指すところの疎結合が実現できています
- 上記の例ではUserRepositoryをスタブなどで差し替えられるためUserServiceのテストが容易になります
欠点
- コードの記述量は増えます
- 実装を挿入する必要があるため、Dependency Injectionなどの仕組みが別途必要になります
まとめ
レイヤー化アーキテクチャとそれを実現するためのDIPの適用を行いました。このアーキテクチャを採用するかどうかはシステムの複雑度にもよると思います。MVCなどのアーキテクチャではModelがドメイン層とインフラストラクチャ層が密に結合した形になっているので、システムがそれほど複雑でなければ素直にそういったアーキテクチャに従うほうがコストがかからないと考えられます。また、レイヤー化アーキテクチャですが、実践ドメイン駆動設計ではこの構造をさらに一般化したヘキサゴナルアーキテクチャを利用して4章の後半以降の解説を行っています。私見ですがヘキサゴナルアーキテクチャの方がモジュールの命名規則などが理に適っている印象があるため、実際に採用する場合はそちらを採用するほうがよいと思います。