勉強日記

チラ裏

PoEAA ch13 Repository

martinfowler.com


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の利用は隠蔽される
      • 【補】利用しないこともある(後述)
  • パターンの意図
    • オブジェクトがデータストア(DBとか)に永続化されていることの隠蔽
    • データストア操作の隠蔽
  • 【所感】Data Mapperとの違いを僕はこう解釈した
    • Data Mapper
      • 利用側は永続化のことを知っている
        • どう永続化されるかは知らない
      • ので、load, saveといったメソッドがinterfaceに定義される
    • Repository
      • 利用側は永続化のことを知らない
        • 「メモリ上のドメインオブジェクトのコレクション」に見える
      • ので、matching(aCriteria), add(domainObject)といったメソッドが定義される

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
        • CriteriaからSQL文の構築
        • SQL文の実行をData Mapperに委譲
      • Metadata Mapping
      • Data Mapper
      • Identity Map
  • Strategyでデータソースを隠蔽する
    • 【所感】GoFのBridgeのほうが近くない?
    • 【補】2019年現在、Repository自体のinterfaceをドメイン層に置くやり方のほうが流行ってる気がする
  • 利用側のコードの可読性・明確さに貢献する
    • HTTPリクエストを解釈し、入力にしたがってCriteriaを構築してRepositoryに渡すだけ

When to Use It

  • 利用側でQuery Objectのセットアップコードが不要になる
    • RDBを意識させない
  • データソースが複数になるとき本領発揮
    • 例: 単体テスト時、in-memoryデータストア用のStrategyを注入する
      • 高速化
      • fixtureが宣言的でわかりやすくなる
        • setUp/tearDownで「DBにINSERT/DELETE文を発行する」必要がなくなる
    • 例2: データソースがRDBではなく、外部APIからXMLSOAPが降ってくる

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
    • 舞台裏で