PoEAA ch10 DataMapper

PoEAA勉強メモ

出典: 


Data Mapper

A layer of Mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself.

  • オブジェクトとRDBとのインピーダンスミスマッチ

    • RDBには…

      • コレクションがない

        • 【補】テーブルをリレーションとして使わないならこの限りではない

          • 第一正規形:「テーブルがリレーションである」
      • 継承がない
    • 両者のスキーマは必ずしも一致しない

      • オブジェクト: ビジネスロジックの表現
      • RDB: データ永続化
  • オブジェクトにスキーマ変換の知識は持たせたくない

    • O/Rのスキーマ間のデータのやりとりはそれ自体が複雑
    • RDBのスキーマの変更がオブジェクトに影響を与える
  • Data Mapper

    • OとRの間接層
    • DomainObject <— DataMapper —> RDB
    • 依存を切り離す

      • Domain ObjectはSQLのことを知らない
      • RDBはそれを利用するオブジェクトのことを知らない
      • Domain ObjectもRDBも、Data Mapperのことを知らない
  • Mapperの一種

    • 【補】他にも、DTOのMapperとかがある

How It Works

  • Domain層とData Source層との分離が本層の主要な役目
  • 考慮すべきこと多し
  • O/Rマッピングのロジックが複雑な場合に有効

    • オブジェクトの1フィールド = RDBの1カラム のような単純なマッピングの場合は冗長
  • 更新系

    • create, update, delete
    • トランザクション境界としてUnit of Workを用いよ
  • オブジェクトグラフの取得について

    • SQLクエリの回数を減らすため、1クエリで複数のオブジェクトのグラフを読み込む

      • 例: PersonにOrderやOrderLineをぶら下げて返す
    • オブジェクトグラフの読み込みは、どこかで打ち切る必要がある

      • さもないと、RDBの中身全部取得することになってしまう
    • 打ち切った先はLazy Load
    • ここにおいて、Domain Modelは「完全にData Mapperを知らない」ことはできない

      • 【補】Domain ObjectにLazy Loadを搭載するということは、何が未ロードになるか知っていなければならない

        • 何が未ロードなのかはData Mapperの知識
  • Data Mapperいくつ作る?

    • 手でコーディングしているならDomain Objectや、そのグラフのrootのクラスにつき1つ

      • 【補】DDD界隈でEntityやAggregateと呼ばれるヤツのことか
    • Metadata Mappingで自動生成しているなら単一でもよい

      • 単一クラスに大量のfindXxxメソッドが生えることになる
  • Identity Maps

    • RDBから読み出したオブジェクトの一意性の維持

      • 【補】1つの主キーに対し、メモリ空間上1つのオブジェクト
    • どこにおく

      • Registryに置く

        • 【補】Singletonみたいなやつ
      • Finderの中に置く

Handling Finders

  • Domain Objectからfindメソッドを呼びたいケース

    • Lazy Loadを適用すれば、Domain Object内での明示的な呼び出しコードは回避可能

      • 【補】associationをたどると暗黙裡にデータ読み込み
    • シンプルなアプリケーションでは大げさ
    • でもDomain ObjectからData Mapperへの依存は避けたい
    • Separated Interfaceを適用せよ

      • Finderインタフェースを切り出す

Mapping Data To Domain Fields

  • Data MapperはDomain Objectのフィールドにアクセスする必要がある
  • でもフィールドをpublicにはしたくない
  • 簡単ではない問題

    • package-private使う?

      • DomainとMapperとを同じパッケージにしてしまうと依存関係が大味になりすぎる

        • Domainに依存するモジュールはMapperにも依存
    • リフレクションで不可視性をバイパスする?

      • 遅い

        • SQLコールに比べれば無視小とも
    • public setter生やす?

      • 工夫は必要

        • 特定のコンテキスト外で呼び出されたら例外送出
        • 特定のコンテキストで使ってほしい旨を名前で表現する

          • 【補】聞いたことのある命名例

            • *ForMapper
            • Reconstruct
  • オブジェクトの生成についても同様の問題がある

    • とるべき道は2通り:

      • rich constructor
      • empty object + Lazy Load
    • 著者は前者好み

      • 構築時点でwell-formedなオブジェクトが得られる
      • immutableにできる

        • フィールドをmodifyするpublicメソッドを生やさない
    • rich constructorの問題点

      • 循環参照

        • 構築時に無限再帰に陥る
      • どう回避する

        • rich constructorをやめ、empty object + Lazy Loadにする

          • Identity Mapに登録があればそれを読み出す
          • なければ空のオブジェクトを生成し、即座にIdentity Mapに登録する
    • empty objectの場合、immutableをどう実現するの問題

      • 先述の解決法に同じ

        • 特定のコンテキスト外で呼び出されたら例外送出
        • 特定のコンテキストで使ってほしい旨を名前で表現する
        • リフレクション

Metadata-Based Mappings

  • マッピングの知識どう表現する

    • コードで明示的に書く
    • Metadata Mappingで自動生成する

When to Use It

  • DBのスキーマとオブジェクトモデルとが異なる道を歩むとき

    • 特に、Domain層にDomain Modelを採用した場合
  • ドメインの複雑性が試金石

    • Domain Model + Data Mapper

      • 複雑なドメインロジックに適する
      • 層が余分に必要
    • Active Record

      • ドメインがとても単純で、アプリケーション開発者がDBモデルも触れるならこちらでも
      • Data MapperのふるまいがDomain Object自身に押し込められた形
      • O/Rマッピングが複雑化したら、別層に切り出すほうがよい
  • 自分で作らずに買うという選択肢

Example

  • 実装してさんざん悩んだことあるから略