ドメイン駆動を読む(第2部 DDDの応用 第6章 インフラのための準備)

  • 実際のデータベースを操作せずに、セーブシナリオのためのテストを書けるようにする
  • 目指すところは、インフラに左右されないコードを書くこと

ライフスタイルとしてのPOCO

  • PIかどうかを見分けるには、「ドメインモデルにインフラ関連の外部DLLに関する参照が含まれているかどうか」を確認するのがはやい
    • 例)O/RマッパーとしてNHibernateを使用している場合、nhibernate.dllに対する参照がドメインモデルのコードに含まれていたら、不吉なにおい


以下のようなものは不吉のにおい
  1. 特定の基底クラス(object以外)の継承

    ドメインモデル開発後、永続記憶に対応しようとしたときに、継承が要求されてドメインモデルを変更しなければならないリスク

  2. 与えられたファクトリだけでインスタンス生成

    ファクトリの強制は、ダーティーチェックで便宜を受けるためであることが多い

  3. コレクションのために特別な(自由に選択できるなら使っていなかった)データ型を使用

    レイジーロードをサポートするために使うことがある

  4. 特別なインターフェースを実装

    永続可能とするためにインフラ提供のインターフェースを(ひとつまたは複数)実装する
  5. 特別なコンストラクタを提供

       
    データベースの値でインスタンスを再構成できるようにするために
  6. 特別な必須フィールドを提供

    インフラがGuidベースのIdフィールドや、intのVersionを要求
  7. 特定の言語要素を避ける

    readonlyフィールドを避ける

  • PIのメリット…TDDにとって好都合である。EntityやValueObjectが明確でクリーンになる
  • リポジトリにとってのPI…リポジトリはインフラを動かさなければならない。
  • 解決策:永続記憶抽象化レイア(NWorkspace)の導入
    • ドメインモデルにRepositoryを置いて、インフラを参照する
    • 参照するのは、永続化フレームワークではなくNWorkspace DLL
    • 同じRepositoryがO/Rマッパーとフェイクの両方を対象に動作する
    • フェイクはインスタンスを永続化せず、メモリ内ハッシュテーブルにインスタンスを格納。初期のリファクタリングに便利
    • インフラベンダーのAPIではなく、素朴なAPIをターゲットとしてRepositoryを書く

セーブのシナリオの処理

  • 論理的なUnitOfWorkを作って、その特徴をセーブに活かしたい
  • RepositoryがUnitOfWorkとやり取りするのを筆者は好む
  • 「ひとつのシナリオ」が「ひとつのUnitOfWork」「ひとつのIdentityMap」を持つべき
  • 集約(Aggregate)はPersistAll()呼び出し時に一貫性の取れた状態になるように設計するべき
  • 物理トランザクションとUnitOfWorkは同じでありたい

フェイクメカニズムの構築

  • トランザクションを非常に重要な概念として扱わなければならない
  • UIプログラマトランザクション管理の仕事を押し付けたくない
  • フェイクメカニズム…2つのレイヤを持つIDマップを使う。
    • 第1レイヤ…現在のシナリオで読み出したすべてのEntityを管理する
    • 第2レイヤ…永続化エンジンをシミュレート
    • GetById()を呼び出したとき、要求された型のIDマップにIDが見つかったらそのまま返す。みつからなければ第2レイヤにフェッチ。返ってきたオブジェクトを第1レイヤにコピーして呼び出し元にオブジェクトを返す。
  • すべてのテストがフェイクと本物のインフラの両方で実行できるのは、早い段階でテストが書けるのでよい目標
  • フェイクでテストできることにより、リファクタリングに積極的になれる

クエリー

  • QueryObjects
  • SELECTですべての行を取得し、取得した行数を数えるようなものはまずい
  • クエリーとキャッシュ
    • GetByQuery()は永続記憶にアクセスする前にIDマップをチェックしない
    • 問題:IDマップ/UnitOfWorkに追加されているが永続化されていないCustomerは、IDで問い合わせれば見つかるがクエリーだと見つからない
    • 解決策:GetByQuery()はデフォルトで暗黙的にPersistAll()を呼んで、その後DBアクセスする
  • クエリーの置き場所
    • Repositoryの中
    • Repositoryのコンシューマの中
    • ドメインモデルの中
      • 型付きQueryObjectを使うことにより、ドメインモデルをカプセル化できる
      • これは柔軟性をさげるが、よい。なんでもできてしまうのはよくない