DDD
REPOSITORIES (p.147) †
一応、要約は完成です。
要約 †
REPOSITORIES †
- 文脈
- 関連を辿ればオブジェクトを見つけられるが、ライフサイクル途中のエンティティや値オブジェクトを見つめるためのスタート地点はどこなのか?
- 問題
- オブジェクトをいじるには参照が必要。参照を得る方法は、以下の3つ。
- オブジェクトを生成する
- 関連を辿って、オブジェクトを見つける(→ OODBでやりすぎた経験あり)
- DBに問い合わせてオブジェクトを取得する
- DB検索はグローバルにアクセスでき、オブジェクトを直接取得することができる。
- 「関連」or「DB検索」は設計判断。結合の分離(=DB検索)と凝集性(=関連)とのトレードオフ。
- 技術的にはDB検索はオブジェクト生成と一緒だが、コンセプト上は全く別。ライフサイクル途上のエンティティの参照を取得しているだけ!("creation"でなく"reconstruction")
- DDDの狙いは、データアクセス技術の複雑さにとらわれることなく、モデルのコンセプトに注力すること。
- 関連の網の目でぐちゃぐちゃになるのも嫌だが、DBアクセスに頼りすぎると、ドメインロジックがSQLやクライアントコードに移ってしまって、ドメインモデルが単なるデータコンテナになっちゃうよ!(→ドメインモデル貧血症)
- 以下については、ここでの問題の対象外にできる
- 一時的な(transient)オブジェクト(値オブジェクトとか)
- 関連で辿った方がいいようなオブジェクト(Person内のAddressオブジェクトとか)
- AGGREGATES内のオブジェクトは、基本的にルートから関連を辿るべき
- ただし、値オブジェクトもDB検索が必要な場合がある
- ユーザのお気に入りに登録された旅程(=値オブジェクト)、"enumeration"(?)など
- そういう場合は、隠れたエンティティ概念を見落としている可能性大
- 解決方法
- 実装上のテクニックはいくらでもある
- QUERY OBJECTS、METADATA MAPPING LAYERS(以上、PofEAA)、FACTORIES
- → 技術的複雑さを取り除いているが、ビジネスを語るための道具ではない
- そこでREPOSITORIES
- =「技術的解決法の部分をカプセル化して、モデルに集中するための概念的なフレームワーク」
- 概念的には、ある型の全オブジェクトの集合。コレクションみたいなものだが、高機能なCRUD、クエリAPIがついているイメージ
- AGGREGATES(集約)のルートに対して、グローバルなアクセスを提供するもの
- IN: クライテリア、パラメータ OUT: オブジェクト、サマリ情報、集計結果
- 期待される結果
- ドメインオブジェクトのライフサイクルを扱う手段が提供される
- アプリ/ドメイン設計とパーシステンス層とがきれいに分離される
- オブジェクトアクセスの意図を明確にできる
- テスト時にダミー実装を差し替えられる
Querying a REPOSITORIES †
- クエリAPIの設計上の選択肢について。次の2つがある
- パラメータをハードコードしたクエリ(DAO系?)
- SPECIFICATION(クライテリア)ベースのクエリ(Hibernate?)
- SPECIFICATIONベース(ii)のときでも、パラメータベース(i)を組み合わせて使うこともある
Client Code Ignores REPOSITORIES Implementation; Developers Do Not †
★ ここはREPOSITORIESを使う上で、非常に重要で面白いところ。
- REPOSITORIESを使うとクライアントコード(ドメインモデル)はきれいになるけど、だからと言って開発者まで内部実装を無視していい訳ではない!
- 開発者は、クエリの裏側で何が起こっているのかを理解して、パフォーマンスを意識しながら使うべき
- 全オブジェクトをロードしててメモリいっぱいになってしまったKyle Brownの例
- また、内部で使うフレームワーク/DBによる制約もある
- RDB使うとオブジェクトの階層をあまり深くできないとか。
- 「内部技術 ⇔ ドメイン設計」の両側からのフィードバックを開発者は常に受けるはず(→ ここが HANDS-ON MODELER 的?)
Implementing a REPOSITORIES †
- REPOSITORYの実装方法は、使うフレームワークや技術によりいくらでもありうる。ここでは大まかな方法だけ。
- 型を抽象化。スーパークラスやインタフェース毎にREPOSITORYを作る。
- 実装の差し替え。クライアントに影響せず、実装を変更できる。キャッシングなどによるパフォーマンスチューニングや、テスト時のモックなど。
- Tx管理はクライアントで。クライアントの方が適切な Unit of Work を知っているから。
- REPOSITORYの共通スーパークラスに基本的なクエリを実装するようなことをしたいが、Javaだと戻り値がObjectになってしまい、クライアント側でキャストしないといけない。ただ、Listの戻り値では必ずキャストが必要なのだが。(佐藤:Java5以前の話)
- その他の設計/実装テクニックの詳細は、PofEAAを参照。
Working Within Your Frameworks †
既存のフレームワークと折り合いをつけるときの話。
- J2EEの場合:
- AGGREGATESルート = EJBエンティティBean
- REPOSITORIES = EJB Home?
- フレームワーク選択の余地がないとき、
- 「フレームワークと戦うな!」
- 肉を切らせて骨を断つイメージ。ダメなところは我慢しつつ、なるべくDDDに近づける。
- フレームワークを自由にせんたくできるとき、
- 好みの設計スタイルにあったORMフレームワークを選びましょう。
The Relationship with FACTORIES †
FACTORIESとREPOSITORIESの比較。
- どちらもオブジェクトを生成するので、同じに見られがち。
- 少なくとも、技術的には正しい。
- ドメインの観点からすると違う。「FACTORIESは新しいオブジェクトを生成する。REPOSITORIESは既存のオブジェクトを見つけだす」
- 2つは厳密に分けるべき。区別することで、FACTORIESからパーシステンスの責務を取り除ける。
- ドメインの観点では、新しいオブジェクトを作ったのか、既存オブジェクトを読み込んだだけなのかの区別が非常に重要!
担当者のつぶやき †
- p.150 ""enumeration," when a type has a strictly limited, predetermined set of possible values." ここがよく分からない。
- 結局REPOSITORIESって、S2DaoやHibernateを直に使っちゃっていいものなのか、それともそうしたORMフレームワークの上にさらに1枚リポジトリの皮を被せるべきなのか、がよく分からない。元々DAOとかデータマッパがデータアクセス層を隠蔽するものだったのに、さらにそれをリポジトリで隠蔽しないといけないとなると、何がなんだか分からなくなってくる・・・
みんなの突っ込み †
- "enumeration" は Java なんかの enum,いわゆる「列挙」ですね.「取り得る値 (列挙子のこと) の集合があらかじめ決められたものに厳しく限定された型」みたいな. -- 小林 (koichik)?
- koichikさんありがとうございます。列挙型みたいなの、というのは分かったんですが、なんでそれがDB検索する必要あるのかがよく分からないんです。 -- sato?
- それは確かに分からない.(^^; 「別のケース」と言われても,それがどういうケースなのかは読み取れない (書いてなさそう) し想像も及ばないですね. -- 小林 (koichik)?
- "Another Case"はp.150 l.5の"There are exceptions"にかかっているのではないでしょうか?必要なのはDB検索というより"a global search access"で、「通常であればVALUE OBJECTを直接ルックアップする必要はないけれども、VALUE OBJECTがとりうる値が列挙体のように定まっている場合には必要」ということだと思います。 -- 和智?
- もちろん「例外」の最初の例とは「別のケース」ということなんですが,その「別のケース」が具体的にどういうケースか分からないと言うことです.例もないし.「VALUE OBJECTがとりうる値が列挙体のように定まっている場合には必要」というのも意味が分からないです.当日詳しくお願いします. -- koichik?