📖

ソフトウェアの柔軟性を保つための依存関係の管理 〜ドメイン駆動設計入門 Chapter 7 を読んで〜

2022/01/06に公開

※※※本記事は、成瀬 允宣氏の著「メイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本」の Chapter7 の要旨を個人の見解でまとめたものです。


結論

ソフトウェアにおいて、オブジェクトやインターフェースを参照するだけで依存関係が発生します。そのため、依存が生まれることは受け入れるしかありません。
本来、柔軟に変更が可能なソフトウェアが依存によって変更が容易ではなくなることを抑えるために依存の方向性をコントロールするようにしましょう。

依存とは(例)

依存関係が発生する例です。

public interface ICookingMachine {
  Food make();
  Food serve();
}

public class CurryMachine implements ICookingMachine {
  public Food make() {
    System.out.println("カレーを作ります");
    return new Food("カレー");
  }

  public Food serve() {
    System.out.println("作ったカレーを提供します");
    return make();
  }
}

であれば、 CurryMachine は、 ICookingMachine に依存し、serve は、 make に依存します。

1. 具象型ではなく、抽象型に依存するようにする

Robert C. Martinによって唱えられた依存関係逆転の原則

High-level modules should not depend on low-level modules. Both should depend on abstractions.
(上位レベルのモジュールは下位レベルのモジュールに依存してはならない。どちらも抽象に依存すべきだ。)
Abstractions should not depend on details. Details should depend on abstractions.
(抽象は詳細(具体的な実装)に依存してはならない。詳細が抽象に依存すべきだ。)
*

この原則に則った場合、具象型から具象型を参照したパターンと具象型から抽象型を参照したパターンとで矢印の向きが逆転します。[1]

直接依存している場合

直接依存している場合

抽象を経由して依存する場合

抽象を経由して依存する場合

各モジュールと同一レイヤー内で抽象型を定義することにより、下位のレイヤーに求めるふるまいを決定できます。
ポイントは抽象型の位置が実際の具象型と同階層のレイヤーにないことです。
(私はinterfaceとclassを同階層のレイヤーにあると考えていました…)

結果として、ControllerでServiceを直接利用するよりも結合度が下がります。
(UserControllerの仕様を変更することなく、UserServiceをTestUserServiceに置き換えることが容易になります)

2. 依存関係をコントロールする

Service LocatorパターンとIoC Container(DI Container)パターンについては、こちらがわかりやすかったのでぜひ読んでください。

脚注
  1. サイトによってRepositoryの位置が違ったりしますが、説明のために簡略化した図なのでレイヤーを分けて書いています ↩︎

Discussion