目標
・ふわっとした理解のDDDについてエヴァンス本を通して体系的に学習する
・各章の要約をアウトプットすることで知識を定着させる
前回の記事
第5章の内容は以下でまとめています。
【第6章】ドメインオブジェクトのライフサイクル
オブジェクトのライフサイクルの中で寿命の長いオブジェクトは、
他のオブジェクトとの間に複雑な依存関係の構築や、不変条件が適用されるような
状態の変化も経験する。
このようなオブジェクトを管理する課題は2つのカテゴリに分類される。
- ライフサイクルを通じた整合性の維持
- ライフサイクル管理の複雑さとモデルの破壊
本章では3つのパターンを通じて、これらの問題に対応する。
集約
・集約
関連するオブジェクトの集まりで、データを変更するための単位。
└ルート
集約に含まれている特定のエンティティで、外部オブジェクトから参照可。
└境界
集約の内部に何があるかを定義するもの。境界内のオブジェクトであれば互いに参照可。
・不変条件
データが変更されるときは常に維持されなければならない一貫性のルール。
集約のメンバ間の関係も含む。
⚪︎集約を実装するためのルール
- ルートエンティティはグローバルな同一性を持ち、不変条件をチェックする責務を負う
- 境界内のエンティティは集約内のローカルな同一性をもつ
- ルートは内部のエンティティへの参照を渡せるが保持はできない
- DBにアクセスできるのはルートのみ
- 集約内部のオブジェクトは、他の集約ルートへの参照を保持できる
- 削除する際は、集約全てを一度に削除する
- 集約内部のオブジェクトに変更がコミットされる時は、集約全体の不変条件を満たす
ファクトリ
・ファクトリ
複雑な組み立て全てをカプセル化(集約)し、ひとまとまりとして生成するインターフェースの提供
を責務としたプログラム要素。
・アトミック
複数の処理を一つの操作として扱い、途中から介入できない性質。
⚪︎優れたファクトリの基本要件
- 生成メソッドはアトミックで、集約の不変条件を強制する
- 生成するのは具象クラスではなく、型に応じて抽象化する
ファクトリとその場所を選択する
通常は集約を中心として配置を行う。
例えば、キノンの集約に要素を追加する必要があれば、その集約のルートに
ファクトリメソッドを生成することもできる。
コンストラクタがあれば良い場合
以下のような状況であればファクトリがなくてもコンストラクタが有利。
・クラスが型である
・クライアントが実装に興味をもつ
・コンストラクタ内部にオブジェクト生成がネストされていない
・構築が複雑でない
・ファクトリと同じルールに従う
インターフェースを設計する
ファクトリを設計っする際には以下の2点を意識する。
- 各操作はアトミックでなければならない
ファクトリとのやりとり一回で必要な情報を全て渡さなければならない。 - ファクトリはその引数と結合する
結合度合は引数を使って調整する。
引数を単純にセットするだけなら弱い依存関係ですむが、
引数から一部を取り出して使用する場合は結合がより緊密になる。
不変条件のロジックはどこへ置くべきか?
不変条件のロジックをファクトリに入れ、生成物をすっきりさせる。
集約のルールに用いると効果的。
エンティティファクトリ対値オブジェクトファクトリ
値オブジェクトファクトリを操作する際には、生成物を完全に記述することを考える必要がある。
一方エンティティファクトリは有効な釉薬を生成するのに必要な本質的な属性だけを受け取る傾向がある。
格納したオブジェクトを再構成する
オブジェクト生成用のファクトリと、再構成用のファクトリの違い
- 再構成に使用されるエンティティファクトリは新しいIDを割り当てることがない
割り当ててしまうと生成時のオブジェクトとの連続性が失われてしまう。 - 再構成するファクトリは、不変条件の違反を違う形で制御する
再構成する場合にはより、システムのどこかにすでにオブジェクトが存在していることを考慮し
より柔軟無対応が必要になる。
リポジトリ
・リポジトリ
DBアクセルの技術と戦略をカプセル化し、オブジェクトで構成されるコレクションをエミュレートする仕組み
リポジトリのメリット
・永続化されたオブジェクトを取得し、ライフサイクルを管理するシンプルなモデルの提供
・アプリケーションとDDDを、DB接続やデータソースから分離
・オブジェクトアクセスに関する設計上のきまりを伝える
・テストコードの実装が容易になる
リポジトリに対して問い合わせる
仕様パターンに音づいたクエリを使用することは、フレームワークを用いてリポジトリを一般化する上で
適したアプローチとなる。
クライアントのコードはリポジトリの実装を無視するが、開発者はそうではない
開発者はリポジトリ内で何が起こっているかを理解しなければならない。
カプセル化されたふるまいを使用した際の影響について理解し熟知しなければならない。
リポジトリを実装する
リポジトリの実装方法
・型を抽象化すること
リポジトリは特定の型のインスタンスを全て含むが、各クラスにリポジトリが必要というわけではない。
・クライアントから切り離す利点を活かす
クライアントと分離しているので、クエリの変更や永続化戦略の切り替えが楽になる。
・トランザクション制御をクライアントに委ねる
リポジトリはコミット等のトランザクション処理を行わずクライアントに委ねる。
フレームワークの範囲内で作業する
フレームワークの機能とDDDが対立してしまった場合は争わず、
DDDを保ちながら詳細を捨てるなど設計スタイルと調和させる。
ファクトリとの関係
ファクトリは新しいオブジェクトを生成し、リポジトリは古いオブジェクトを使用する(オブジェクトの再構築)
という責務の違いがある。
関係データベースに合わせてオブジェクトを設計する
・データベースをオブジェクトの格納先として見る場合にはデータモデルとオブジェクトを
かけ離れたものにしてはならない。そのためにオブジェクトの豊かさを若干犠牲にすることも厭わない。
・オブジェクトシステムの外部にあるプロセスからはオブジェクトの格納先にアクセスしてはならない。
オブジェクトが強制する不変条件に違反する可能性があるため。
まとめ
オブジェクトライフサイクルの管理方法について学ぶことができた。
文章だけでは抽象的すぎて理解が難しいものもあると思うが、
本の中では様々なクラス図を用いて説明されており理解を助けてくれた。
次は「言語を使用する:応用例」について学んでいく。
参考になったらいいねやコメントおまちしています!!