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