PoEAA ch13 Repository
Repository
Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
- Domain ModelとData Mapper利用時
- Data Mapperへの問い合わせを一元管理する間接層を1つ設けることは有用
- 【補】前節Query Objectもこの類
- 問い合わせロジックの重複の最小化
- ドメインオブジェクトのコレクションのようにふるまう
- Repositoryのクライアントは、取得したいオブジェクトの仕様を宣言的に指定する
- DataMapperの利用は隠蔽される
- 【補】利用しないこともある(後述)
- DataMapperの利用は隠蔽される
- パターンの意図
- オブジェクトがデータストア(DBとか)に永続化されていることの隠蔽
- データストア操作の隠蔽
- オブジェクト指向なインタフェースの提供
- 【所感】Data Mapperとの違いを僕はこう解釈した
- Data Mapper
- 利用側は永続化のことを知っている
- どう永続化されるかは知らない
- ので、
load
,save
といったメソッドがinterfaceに定義される
- 利用側は永続化のことを知っている
- Repository
- 利用側は永続化のことを知らない
- 「メモリ上のドメインオブジェクトのコレクション」に見える
- ので、
matching(aCriteria)
,add(domainObject)
といったメソッドが定義される
- 利用側は永続化のことを知らない
- Data Mapper
How It Works
- 自分で作るというよりは、市販のO/Rマッパーについてくるのを見かけることが多いだろう
- Query Objectを自分で作ったならば、Repositoryパターンまでもう一息
- 複雑な裏側と裏腹に、インタフェースはただひとつ
matching(criteria)
: ドメインオブジェクトのコレクションを返す- 「1つだけ返す版」があってもいい
soleMatch(criteria)
findByXxId(id)
とか
- ドメインオブジェクトがRepositoryに格納されているかどこかに取りに行くのか、利用側には知る由もない
- かといって
all()
で何十万レコードもデータフェッチしてしまうのは良くない
- かといって
- DataMapperの、用途特化の
findXx()
メソッドを、matching(criteria)
で置き換える - Query Objectとの比較
- 共通点
- Criteriaを構築すること
- 相違点
- Query Object
- SQLクエリの実行を意識させる
- Repository
- Criteriaを満足するオブジェクトを良しなに取得する
- Query Object
- 共通点
- 中では本書であげた様々なパターンが絡み合っている
- Strategyでデータソースを隠蔽する
- 利用側のコードの可読性・明確さに貢献する
- HTTPリクエストを解釈し、入力にしたがってCriteriaを構築してRepositoryに渡すだけ
When to Use It
- 利用側でQuery Objectのセットアップコードが不要になる
- RDBを意識させない
- データソースが複数になるとき本領発揮
Further Reading
- Evans読め
コード例
- 略
- クラス図
- 特記事項
- 用途特化のfinderを生やしても良いよう(p.326)
- Strategy Patternの適用
- 【所感】何度でも言うけどBridge Patternだろこれ
@startuml package Domain { class Person class Criteria abstract Repository{ - strategy RepositoryStrategy + matching(aCriteria Criteria): List<Person> } class PersonRepository interface RepositoryStrategy { + matching(aCriteria Criteria): List<Person> } } package DataSource { class RelationalStrategy class InMemoryStrategy class QueryObject{ criteria: List<Criteria> - toSql():string + addCriteria(aCriteria: Criteria) + execute(): List<Person> } class DataMapper{ + findWhere(whereClause: string): List<Person> } class DB } PersonRepository -up-|> Repository Person ..right..> PersonRepository Person ..> Criteria : "<<create>>" Repository o-right-> RepositoryStrategy : "<<delegate>>" RelationalStrategy ..up..|> RepositoryStrategy InMemoryStrategy ..up..|> RepositoryStrategy RelationalStrategy ..> QueryObject : <<create>> QueryObject .ri.> DataMapper QueryObject o-- Criteria DataMapper ..> Person : "<<create>>" DataMapper .ri.> DB @enduml
英語
- benefactor
- 支援者
- behind the scenes
- 舞台裏で