# DDD輪読会 II-6 2022/06/09 [@kdnakt](https://twitter.com/kdnakt) --- ### 今日の範囲 - エリック・エヴァンスのドメイン駆動設計 - 6章 ドメインオブジェクトのライフサイクル - 集約(AGGREGATES) - ファクトリ(FACTORIES) - リポジトリ(REPOSITORIES) - RDBに合わせたオブジェクト設計 --- ### オブジェクトのライフサイクル ```mermaid flowchart LR 誕生 -->|生成|アクティブ アクティブ-->|修正|アクティブ アクティブ-->|格納|DB DB-->|再構成|アクティブ アクティブ-->|アーカイブ|ファイルetc アクティブ-->|削除|死 ファイルetc-->|削除|死 ``` ---- #### オブジェクト管理の課題 1. ライフサイクルを通じて 整合性を維持できるか 2. 複雑なライフサイクルで モデルが侵食されないか ---- #### 課題を解決する3つのパターン - 集約: 境界を定義し、整合性を保つ - ファクトリ: 複雑なオブジェクトの生成、再構成 - リポジトリ: 永続層へのアクセスをカプセル化 ---- #### ファクトリとリポジトリ - それ自体はドメインに由来しない - モデルオブジェクトを操作可能に - モデル駆動設計を完成させる --- ### 集約(AGGREGATES) - 不変条件を維持する必要性 - =TXのスコープとデータの一貫性を維持する - 🙅‍♂️技術的な問題 - 🙆‍♀️モデル、ドメイン理解の問題 - 境界が未定義 ---- #### 購入注文の整合性 - シンプルなモデル - 不変条件:注文品目総額 <= 限度額 ```mermaid classDiagram class 購入注文 購入注文 : +int 限度額 class 購入注文品目 購入注文品目 : +int 数量 購入注文品目 "*" <|-- "1" 購入注文 class 商品 商品 : +int 価格 購入注文品目 -- 商品 ``` ---- ##### 相互に関連した3つの問題 1. 不変条件の強制が不十分 - ロックの範囲はどこか? 2. 変更管理がモデル化されていない - 商品価格の変更などどこまで関係を辿るべきか? 3. データベースの共有 - 別のトランザクションにおける同時編集 ---- ##### ビジネス知識でモデルを改良する - 商品:共有、変更頻度低 - 商品価格変更:必ずしも購入注文に反映不要 ```mermaid classDiagram class 購入注文 購入注文 : +int 限度額 class 購入注文品目 購入注文品目 : +int 数量 購入注文品目 : +int 価格 購入注文品目 "*" <|--o "1" 購入注文 class 商品 商品 : +int 価格 購入注文品目 --|> 商品 ``` ※◇-->:集約を表す記号 --- ### ファクトリ(FACTORIES) :::warning - オブジェクトや集約の生成が複雑 - オブジェクトの内部構造が剥き出し ::: - :no_good: 複雑なオブジェクトがそれ自体を生成 - :no_good: クライアントがオブジェクトを生成 - :ok_woman: ファクトリによるカプセル化 ---- #### ファクトリの責務 - 複雑なオブジェクトや集約を生成/再構成する - オブジェクトの具象クラスをクライアントから隠す - ひとまとまりの集約を生成し不変条件を強制する ---- #### 集約の拡張をカプセル化するファクトリ - 既存の集約に要素を追加するケース - 集約内部実装をクライアントから隠蔽 - ファクトリが集約の整合性を保証 - 集約をリファクタリングするケース - 取引注文->売り注文/買い注文 ---- #### コンストラクタが適切なケース - 構築が複雑でない - インターフェースのない型である - クライアントが実装を知る必要がある - 例)ストラテジーパターン - 例)Javaのコレクション ---- #### ファクトリのメソッドシグネチャ設計 - 各操作をアトミックに - 1回で全ての引数を渡す - 生成失敗時のコーディング標準 - NULL?例外? - 引数と結合する - 引数は具象クラスでなく抽象型を ---- #### 再構成のためのファクトリ - DB:オブジェクトをシリアライズして保存 - メソッドの情報などは失われる - 利用時に組み立てが必要 - 例)ORM, XMLパーサ - 生成時のファクトリとの違い - 新しい識別子を割り当てない - 不変条件の取り扱い --- ### リポジトリ(REPOSITORIES) - オブジェクトを利用するには参照が必要 - どうやって参照を手に入れる? 1. オブジェクトを生成する 2. 別オブジェクトから関連を辿る - 最初のオブジェクトはどこから手に入れる? ---- #### RDBから再構成する - クエリによりオブジェクトを再構成 - 全てのオブジェクトの関連は不要 - 検索で直接到達できる - 「検索vs関連」のトレードオフ ---- #### モデルが無視される危険性 - クライアント開発者:ドメイン層縮小しがち - SQLクエリを構築 - 関連を追加 - 集約ルート以外から内部オブジェクトにアクセス - モデル駆動設計が不明瞭になる ---- #### DBアクセスに起因する課題への対処 - リポジトリパターン - クエリオブジェクト - メタデータマッピング層 - ファクトリによる再構成 - DB技術をカプセル化しモデルに焦点を戻す - クライアントはモデルの言葉でアクセス - モックによりテストも容易 - 開発者が実装を無視するのはNG - 例)集計に全データをロードしOOM ---- #### リポジトリのインターフェース設計 1. 特定のパラメータを受け取る - クエリはハードコード - 例)getOrderById 1. 仕様パターン - クライアントが仕様を記述(詳細は9章 ---- ##### 仕様パターンの例 - JPAのCriteria API ![](https://i.imgur.com/ygtv2mZ.png) ---- #### リポジトリを実装する - 型を抽象化する - :no_good: 具象クラスごとにリポジトリ - クライアントから切り離す - 性能の最適化 - テスト時のインメモリモック - トランザクションはクライアントが制御 - リポジトリをシンプルに保つ - フレームワークの範囲内で - ドメイン駆動設計と混ぜすぎない ---- #### ファクトリ - リポジトリ:データからオブジェクトを作る - ある意味ではファクトリ - モデル的にはファクトリではない - 生成と再構成は別 - オブジェクトのライフサイクル - 初期:ファクトリが生成 - 中期以降:リポジトリが再構成 - ファクトリに処理を委譲 - リポジトリの責務を明確化 --- ### RDBに合わせたオブジェクト設計 - DBはオブジェクトでないシステム構成要素 - それぞれが歩み寄る必要性 - モデルの豊かさを犠牲に - 選択的な非正規化も必要 - オブジェクトシステム外からDBにアクセスしないこと - 不変条件への違反 - オブジェクトのリファクタリング困難 ---- #### ユビキタス言語による結びつき - オブジェクトモデルと関係モデルは別物 - ユビキタス言語で単一のモデルができる - オブジェクトの要素の名前や関連に注意 - マッピングツールによっては微妙に異なる関係を生む
{"metaMigratedAt":"2023-06-17T02:14:27.409Z","metaMigratedFrom":"YAML","title":"DDD輪読会 II-6","breaks":true,"slideOptions":"{\"transition\":\"slide\"}","contributors":"[{\"id\":\"df36d0f0-b67e-41ac-96b3-f3988326d230\",\"add\":4072,\"del\":237}]"}
    483 views