Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ドメイン駆動設計と依存性逆転の原則

 ドメイン駆動設計と依存性逆転の原則

社内エンジニアの方々に,ドメイン駆動設計と依存性逆転の原則について布教しようと試みましたʕ◔ϖ◔ʔ

関連テーマ(依存関係と依存オブジェクト注入):
https://speakerdeck.com/hiroki_hasegawa/yi-cun-guan-xi-toyi-cun-obuziekutozhu-ru

個人リポジトリはこちら:
https://github.com/hiroki-it

技術ノートはこちら:
https://hiroki-it.github.io/tech-notebook-mkdocs/about.html

More Decks by Hiroki Hasegawa (長谷川広樹)

Other Decks in Programming

Transcript

  1. ドメイン駆動設計 と 依存性逆転の原則 株式会社ユニクエスト 長谷川広樹 github.com/hiroki-it

  2. 自己紹介 長谷川広樹(はせがわひろき) ▪ 経歴 『生き物の研究者になるぞ! ▶ IT技術おもしろいな... ▶ エンジニアになるぞ!』 を経て,SWE

    になった. 現在は,SRE をやっている. ▪ 関心のある技術領域 拡張性,保守性,可読性を高める技術: ドメイン駆動設計,アーキテクチャ,クラウドインフラ,IaC,コンテナ,CI/CD,Go,etc... github.com/hiroki-it
  3. 目次 ▪ なぜ,我々は依存関係を理解する必要があるのか ▪ 依存(前回のおさらい) ▪ 依存オブジェクト注入の種類(前回のおさらい) ▪ ドメイン駆動設計の原著のアーキテクチャ ▪

    インフラ層で使われるデザインパターン ▪ レイヤードアーキの問題点 ▪ 解決策 ▪ ドメイン駆動設計のアーキテクチャ変遷 ▪ SOLIDの原則 ▪ B:実装が抽象に依存するべき ▪ A:上位と下位のレイヤーの両方が抽象に依存するべき ▪ 『依存性逆転の原則』の成立 ▪ 改めて,アーキテクチャを見る
  4. なぜ,我々は依存関係を理解する必要があるのか オブジェクト間の依存関係を 意識しながら 実装されてますでしょうか??

  5. なぜ,我々は依存関係を理解する必要がある? 基本的なMVCから始まりアーキテクチャには オブジェクト間の依存関係を工夫して 保守性の高いシステムを開発しようとする思想がある 依存関係を理解しなければ アーキテクチャを正しく使えない!(持論)

  6. 依存(前回のおさらい) ▪ 依存とは クラスAがクラスBを引数/返却値として使用 『 A 』は『B 』に依存 ▪ クラス図における依存

    (例)UserクラスはUserNameクラスを使用 <?php namespace App\Domain\User\Entity; use App\Domain\User\ValueObject\UserName; final class User { // UserNameに依存 public function method(UserName $name): string { return "Hello! " . $name->getName(); } }
  7. 依存オブジェクト注入の種類(前回のおさらい) ▪ コンストラクタインジェクション constructメソッド経由で依存対象を渡す. ▪ メリット constructメソッドはインスタンス生成時のみ有効. セッターを完全に無くした場合に インスタンス生成後に状態を変更不可能. 不変的な(Immutable)オブジェクト

    より安全なシステム <?php namespace App\Domain\User\Entity; use App\Domain\User\ValueObject\UserName; final class User { private UserName $name; public function __construct(UserName $name) { $this->name = $name; } } <?php $name = new UserName(); $user = new User($name); // インジェクション
  8. ドメイン駆動設計の原著のアーキテクチャ ドメイン層 インフラストラクチャ層 プレゼンテーション層 ユースケース層 ※順にコンストラクタインジェクションを実行していく ▪ プレゼンテーション層  ・データの必須や書式に関するバリデーション  ・レスポンス構造の整形

    ... ▪ ユースケース層  ・システムの振舞(ユースケース)を具現化 ... ▪ ドメイン層  ・要件定義で作成したドメインモデル図を具現化  ・ビジネス上ありえない値をバリデーション ... ▪ インフラストラクチャ層  ・Active Recordではなく,リポジトリを使う  ・データを取得し,ドメインモデルを作成 ... レイヤードアーキ
  9. インフラ層で使われるデザインパターン ▪ リポジトリパターン  ・極論,DBは何でもよい.  ・複数のテーブルからデータを取得し,   ドメインモデルを作成.  ・ドメインモデル間の依存関係の設計が自由  ⇒ ドメイン駆動設計に合う ▪

    ちなみに,Active Recordパターン  ・モデルが特定のテーブルに関連づけられる.  ・テーブル間のリレーションの設計が,   そのままドメインモデル間の依存関係になる.  ⇒ ドメイン駆動設計には合わない 引用: https://terasolunaorg.github.io/guideline/public_review/ImplementationAtEachLayer/DomainLayer.html
  10. レイヤードアーキの問題点 リポジトリパターンを採用しても インフラ層への依存関係を 完全に除去できない インフラ層の修正がドメイン層に影響 ビジネスに影響 ドメイン層 インフラストラクチャ層 プレゼンテーション層 ユースケース層

    ドメイン層は他レイヤーに依存させたくない
  11. 解決策 ~ 先人の知恵 ~ 『依存性逆転の原則』を組み込んだら良いのでは?? ドメイン層 インフラストラクチャ層 プレゼンテーション層 ユースケース層 矢印の向きを揃えると,インフラ層が最上位になる

    ドメイン層 インフラストラクチャ層 プレゼンテーション層 ユースケース層
  12. ドメイン駆動設計のアーキテクチャ変遷 オニオンアーキ 原著から変遷して,依存性逆転の原則が組み込まれた 引用: ・https://little-hands.hatenablog.com/entry/2017/10/11/075634 ・https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html クリーンアーキ ドメイン層 インフラストラクチャ層 プレゼンテーション層

    ユースケース層 レイヤードアーキ
  13. SOLIDの原則 ▪ SOLIDの原則とは オブジェクト指向プログラミングにおいて, スケーラビリティ,保守性,可読性を高めるための5つの方法のこと ▪ 5原則 ・S:Single Responsibility Principle(単一責任の原則)

    ・O:Open Closed Principle(オープン・クローズドの原則) ・L:Liskov Substitution Principle(リスコフの置換原則) ・I:Interface Segregation Principle(インターフェース分離の原則) ・D:Dependency Inversion Principle(依存性逆転の原則)
  14. 依存性逆転の原則 ▪ 依存性逆転の原則とは  A:上位と下位のレイヤーの両方が抽象に依存するべき.  B:実装が抽象に依存するべき. ▪ レイヤードアーキに組み込むメリット  ・ドメイン層が他レイヤーに依存しなくなる.  ・ユースケース層に影響を与えずに,インフラ層のリポジトリクラスを交換しやすくなる.   ⇒

    テスト時のみ実装リポジトリクラスをモックリポジトリへのエイリアスとすれば,     DBにアクセスしないユニットテストが可能.(モックリポジトリは値を返却するだけ)   ⇒ リプレイス時に異なるリポジトリクラスに交換可能.
  15. B:実装が抽象に依存するべき <?php namespace App\Domain\Foo; interface FooRepositoryIF { // IDを元にドメインモデルを一つ取得 public

    function findById(FooId $id): Foo; // IDを元にドメインモデルを全て取得 public function findAll(): array; } <?php namespace App\Infrastructure\Foo; class FooRepositoryImpl implements FooRepositoryIF { public function findById(FooId $id): Foo // 実装を強制 { } public function findAll(): array { } } 実装が抽象(インターフェース)に依存 ※ 他言語でも類似する機能を使う インターフェースリポジトリ 実装リポジトリクラス
  16. A:上位と下位のレイヤーの両方が抽象に依存するべき FooRepositoryIF FooRepositoryImpl FooController FooInteractor ⇒ インターフェースリポジトリはドメイン層に置く 下位レイヤーが抽象(インターフェース)に依存 ⇒ 実装リポジトリクラスはインフラ層に置く

  17. A:上位と下位のレイヤーの両方が抽象に依存するべき ユースケース層のクラスが ドメイン層のインターフェースリポジトリに依存するには どのようにすればよい? ユースケース層で使う実装リポジトリクラスのデータ型を インターフェースリポジトリとして扱えればよい

  18. A:上位と下位のレイヤーの両方が抽象に依存するべき PHP(クラス),Java(クラス),Go(構造体)... 実装リポジトリのデータ型を インターフェースリポジトリとして扱うことが可能

  19. A:上位と下位のレイヤーの両方が抽象に依存するべき <?php namespace App\Interactor\Foo; final class FooInteractor { private FooRepositoryIF

    $fooRepositoryIF; public function __construct(FooRepositoryIF $fooRepositoryIF) // インターラクタークラスはリポジトリに依存 { $this->fooRepositoryIF = $fooRepositoryIF; // データ型はドメイン層のインターフェースリポジトリ } public function getFoo(FooId $id): Foo { return $this->fooRepositoryIF->findById($id); // インターフェースを経由してメソッドをコール } }
  20. A:上位と下位のレイヤーの両方が抽象に依存するべき FooRepositoryIF FooRepositoryImpl FooController FooInteractor ⇒ インターフェースリポジトリはドメイン層 上位と下位レイヤーの両方が抽象(インターフェース)に依存 ⇒ 実装リポジトリクラスはインフラ層

    ⇒ ユースケース層はドメイン層を使用する依存関係
  21. 『依存性逆転の原則』の成立  A:上位と下位のレイヤーの両方が抽象に依存するべき.  B:実装が抽象に依存するべき. AとBを実現できたため 『依存性逆転の原則』が成立

  22. 改めて,アーキテクチャを見る オニオンアーキ 『依存性逆転の原則』がわかるぞ! クリーンアーキ

  23. 余談 依存性の原則ではないが,上位レイヤーが抽象に依存

  24. 最後に テーマと関係ないけど Gopherくん超かわいい!!! by Takuya Ueda (https://twitter.com/tenntenn) The Gopher character

    is based on the Go mascot designed by Renée French.