依存関係の逆転
from 手を動かしてわかるクリーンアーキテクチャ
LT;DR
Clean Architecture は 「関心の分離 を行い、依存の向きを中心(ドメイン層)にする」というアーキテクチャを設計するうえで守るべきルール
このルールを遵守するために 単一責任の原則 と 依存関係逆転の原則 という考え方が必要
Hexagonal Architecture は Clean Architecture の具体的な例である
アプリケーションのコアは、外部とのやり取りを行う アダプタ を ポート を介して操作する
これにより、アプリケーションのコアのは外部の関心事から隔離されているため、自由に設計できる
hr.icon
単一責任の原則(SRP)
SOLID原則 の 1 つ(S)
「コンポーネントを変更する理由は、1つだけになるようにすべきである」という原則
「1 つのことだけを行う」と解釈されることが多いが、大事なのは「変更する理由が 1 つしかない」点である
変更する理由が 1 つしかなければ、それ以外の理由でソフトウェアが変更されるとき、そのコンポーネントを変更対象から外せる
変更する理由は 依存関係 によってもたらされる
https://scrapbox.io/files/66bcadee711f98001c250211.png
A は(直接的または間接的に)すべてのコンポーネントに依存している
→ 変更する理由が生じるのは、依存のどれかに変更する理由が生じた場合
E はどこにも依存していない
→ 変更する理由が生じるのは、自身の機能に対して変更が要求された場合のみ
SRP を遵守していない コードベース は、時間の経過とともに変更が難しくなる
各コンポーネントが抱える「変更する理由」が増えていくため
最終的に、あるコンポーネントに変更を加えると、他のコンポーネントが機能しなくなることが多発する
The Nature Of Complexity#657a7e5a75d04f00000cab65
依存関係逆転の原則 (DIP)
SOLID原則 の 1 つ(D)
「コードベース内の依存関係は、いかなるものであっても方向性をひっくり返す(逆転する)ことができる」という原則
これにより、ドメイン層 の 永続化層 への依存を逆転させることができる
逆転 = 永続化層がドメイン層に依存する
適用前
https://scrapbox.io/files/66bc8c9c2b22db001d0e8a37.png
適用後
https://scrapbox.io/files/66bdf3c1fd4964001cc274f0.png
Clean Architecture
2012年に Robert C. Martin によって提唱された概念
Clean Architecture はアーキテクチャパターンではない radish-miyazaki.icon
https://scrapbox.io/files/669da99f8ce322001c3f132a.png
親の顔より見た図 radish-miyazaki.icon
「すべての依存は中心に向かって行われるようにする」
これを実現するために、依存関係逆転の原則 を用いてすべての依存の方向がドメイン層(中心)を向くようにする
一番中心にあるのは エンティティ
エンティティの外側の ユースケース はエンティティを操作するコンポーネント
一般的に サービス と呼ばれる
単一責任の原則 を遵守し、扱うものの粒度を細かくする
これにより、ユースケースが見つけづらい問題 の肥大化したサービスになるのを回避する
コントローラ は ビジネスルール をサポートするコンポーネント
ビジネスルールをサポートする = 永続化や UI に関すること
アダプタ を用いて、DB などの外部コンポーネントとのやり取りをする
メリット
ドメイン層のコードは、永続化や FW に何が使われているのか知る必要がなくなる
これにより、開発者はドメイン層でビジネスルールの実装に注力できる
ドメイン層のコードだけを用いて モデリング できるので、DDD との相性も良い
責務もしっかりしているので、どこに何を実装するのか考える必要もない radish-miyazaki.icon
デメリット
各層で エンティティ を表す独自モデルを持つ必要があり、変換する手間がある
DTO を用いた変換
e.g. ORM
ORM の エンティティ はそのままドメイン層では使えない
そのため、同じエンティティを持つクラスを別々に持つ必要がある
しかし、ドメイン層が永続化層にエンティティを渡す場合(またはその逆)に変換する必要がある
ドメイン層と Web 層 以外でも同様のことが起きる
ドメイン層と永続化層とを密結合させる「モデルの変換を行わない」という戦略を取ることも
境界を超える際のモデルの変換
ヘキサゴナルアーキテクチャ
2005 年に Alistair Cockburn によって考案されたアーキテクチャ
https://scrapbox.io/files/66bdfe825e5dc7001db3b3be.png
六角形の中には、ドメイン層 の エンティティ とそのエンティティを操作するユースケースが含まれる
アプリケーションのコア
六角形は外に向かって依存していない
「すべての依存は中心に向かって行われるようにする」 というルールに従っている
六角形の外には様々な アダプタ が配置されている
このアダプタを通して、外部システム(Web や DB など)とやり取りを行う
図左側のアダプタ: アプリケーションを呼び出すための受信アダプタ
図右側のアダプタ: アプリケーションによって呼び出される送信アダプタ
依存関係逆転の原則 で依存性を逆転させる radish-miyazaki.icon
アプリケーションのコアとアダプタとやり取りは、ポート と呼ばれるインタフェースを介して行う
受信ポート
受信アダプタによって呼び出される
アプリケーションのコアにあるユースケースで実装される
送信ポート
ユースケースから呼ばれる
アダプタによって実装される
1 つのポートが複数のアダプタによって実装されることもある
外部システムと実際にやり取りをする送信アダプタによって実装されることもあれば、モックとして実装されることもある
ポート と アダプタ を用いてやり取りをするので、ポート & アダプタ と呼ばれることもある
ヘキサゴナルアーキテクチャはアプリケーションのコアが何を行うか明確にするため、アプリケーションのコアが外部へのインタフェース(ポート)を定義し、所有するようになっている
依存性に沿っている radish-miyazaki.icon
レイヤードアーキテクチャ の場合は逆だったが…
データベース中心の設計になってしまう問題#66bc93f675d04f0000e755da
これにより、アダプタがどのようにしてアプリケーションのコアと連携し、自身の処理を行うのかがポートを介して決まる
ヘキサゴナルアーキテクチャ と 層
ヘキサゴナルアーキテクチャ も複数の 層 を持つように構成できる
warning.icon ここで示す例はあくまで一例
重要なのは、アプリケーションのコアのは外部の関心事から隔離されているため、自由に設計できる点
https://scrapbox.io/files/66be99ca385dcd001d348a5b.png
一番外側には アダプタ層 を作成して、外部システムとの間でデータの変換を行う
アダプタ層の内側に アプリケーション層 を作成する
ポート を定義することで、アプリケーションのコアとやり取りするためのインタフェースがそこに定義される
アプリケーション層の内側に、ドメイン層を作成する
ドメイン層には、ビジネスルールが実装された エンティティ を配置する
ビジネスロジック は ユースケース を実現するクラスや エンティティ に実装される
ユースケース を実現するクラス = ドメインサービス
ユースケースを 1 つだけ実装したクラス
場合によっては、ユースケースを複数実装する戦略もあり
ユースケースの組み合わせを使う頻度が高く、まとめて扱ったほうが 保守容易性 が高い場合
アプリケーションサービス は、様々な ドメインサービス をどのように呼び出すのか調整する サービス
ポートとドメインサービスの間に位置し、ドメインサービスを外部から守ったり、ドメインサービス同士のやり取りを調整したりする
warning.icon 本書でのドメインサービスやアプリケーションサービスは DDD における用語とは少し異なる
本書でのドメインサービス
DDD における アプリケーションサービス が ドメインサービス に取り込まれた形になっている
「ユースケースを実現するもの = ドメインサービス」という位置づけ
本書でのアプリケーションサービス
必ずしもユースケースを実現するのに必要ではないもの
DDD でのドメインサービス
エンティティ や 値オブジェクト で表現できない重要なプロセスを表現するオブジェクト
必ずしもユースケースを実現するのに必要ではない
DDD におけるアプリケーションサービス
ドメインオブジェクト を調整するもの
アプリケーションにおける関心事(e.g. トランザクション)を扱うもの