DDD
担当:佐藤匡剛
Applying and Implementing SPECIFICATION (SPECIFICATIONの適用と実装) (p.227-235) †
要約 †
227-1 (Much of the value...)
- オブジェクトの仕様を決める必要が出てくるのは、次の3つの目的から
- 妥当性検証(validation):オブジェクトがある条件を満たしているか
- 選別条件(selection):コレクションの中からオブジェクトを選ぶ条件
- 生成条件(creation):ある条件を満たしたオブジェクトの生成
227-2 (These three uses...)
- 上記3つは概念レベルでは同じもの
- SPECIFICATIONパターンという形で統一して捉えないと、同じルールであるべきものが別々の形で色々なところに散らばってしまう
- SPECIFICATIONパターンによって、実装はバラバラになるとしても、モデル上は一貫性を保つことができる
Validation (妥当性検証) †
228-1 (The simplest use of...)
- SPECIFICATIONの最もシンプルな用法が妥当性検証
- 請求書についての仕様(Invoice Spec.)の例
- 延滞請求書の仕様(Delinquent Invoice Spec.)、高額請求書の仕様(Big Invoice Spec.)・・・
228-2 (Now, suppose we need to...)
- 営業のために、顧客に延滞請求書があるかどうかを判定するメソッド(accountIsDelinquent?(Customer))を追加してみる
Selection (or Querying) (選別(またはクエリ)) †
229-1 (Validation tests an individual...)
- 妥当性検証は1つのオブジェクトがある条件を満たしているかを確認するもの
- オブジェクトのコレクションの中から、ある基準(criteria)にマッチするサブセットを取ってくる、というニーズもある
- 同じSPECIFICATIONというコンセプトで捉えられるが、実装上のポイントは異なる
229-2 (Suppose there was an application...)
- 延滞請求書のある顧客をすべてリストするというアプリ要求を考えてみる
- まずはインメモリのInvoice Repositoryでこの要求を実装する(selectSatisfying(InvoiceSpecification?)メソッド)
229-3 (So a client could...)
230-1 (That line of code establishes...)
- このクライアントコードは背後にあるコンセプトを伝えている
- 実際には請求書(Invoice)オブジェクトは数千もあって、RDBに格納されている
- RDBとのやりとりという技術的な部分に圧倒されて、モデルがフォーカスを失うことが多い
230-2 (Relational databases have...)
- RDBの強力な検索機能を活用するにはどうすればいいか?
- MODEL-DRIVEN DESIGNはモデルと実装との一致を要求するが、一致しさえすれば実装は何でもよい
- SPECIFICATIONはSQLを使って実装すればいい
230-3 (Here is a simple...)
- InvoiceSpecification?#asSql()メソッドを作り、DelinquentInvoiceSpecification?サブクラスでそれを実装する
231-1 (SPECIFICATIONS mesh smoothly with...)
- SPECIFICATIONはREPOSITORYと上手く混じり合う
- ドメインオブジェクトにクエリアクセスを提供し、DBインタフェースをカプセル化できた
231-2 (Now this design has...)
- しかし、この設計には問題あり! ドメイン層にテーブル構造の詳細が漏れ出ている!
- Invoice、Customerのマッピングに関する情報がInvoiceSpecification?とInvoiceRepository?に散らばっている
- インフラ層でORマッパを使えばそれも解決できるよ
231-3 (When the infrastructure doesn't...) !!! ここはちょっと難しい !!!
- インフラ層が助けてくれない場合は、リファクタリングしてSQLをInvoiceSpecification?からInvoiceRepository?に移せばよい
- InvoiceRepository?に選別ルールそのものを埋め込まないために、ここではダブルディスパッチを使う
232-1 (The asSql() method on...)
- さっきのasSql()はsatisfyingElementsFrom?(InvoiceRepository?)に置き換える
232-2 (This puts the SQL in...)
- これでSQL自体はREPOSITORYに置きつつ、クエリのルールはSPECIFICATIONに置くことができた
232-3 (The REPOSITORY now has...)
- 今度はREPOSITORYに非常に特殊なクエリメソッドが出来てしまった
- それでもいいけど、もし期限切れ(overdue)請求書と延滞(delinquent)請求書とがそれほど数に違いがなければ、REPOSITORYにもうちょっと汎用的なクエリだけを作って対処するのもあり
- InvoiceRepository?#selectWhereGracePeriodPast?(Date) -> selectWhereDueDateIsBefore?(Date) に変更
233-1 (We'll take a performance hit...)
- このコードだとInvoiceを多めに読み込んでループして処理しているので、パフォーマンス上の問題が出る可能性がある
- パフォーマンスを犠牲にしてでもベターな責務の配置をした方がいいかは、状況しだい
233-2 (Sometimes, to improve...)
- パフォーマンスやセキュリティ上の理由から、ストアドプロシージャでクエリを実装することもある
- その場合は、SPECIFICATIONは単にストアドプロシージャにパラメータを渡すだけ
- 実装がどうであれ、モデルは常に一貫している
234-1 (This discussion barely...)
- ここまでではSPECIFICATION使ってDBクエリをする大変さのちょっとしか扱っていないが、ひとまず終了
- PofEAAのRepositoryパターンも読んでおいて(SPECIFICATIONパターンについての話も載っている)
Building to Order (Generating) (オーダーメイド(生成)) †
234-1 (When the Pentagon wants...)
- ペンタゴンが新しい戦闘機を欲しいときは、役人が戦闘機の仕様を書く
- マッハ2を出せること
- 1800マイル飛べること
- 費用は5000万ドル以下
- この仕様は、飛行機の設計でもなければ、まして飛行機そのものでもない
- 航空会社がその仕様をもとに飛行機を設計する
- 仕様さえ満たせば、設計は会社毎に別々だっていい
234-2 (Many computer programs...)
- コンピュータプログラムでも、仕様に基づいて何かを生成する、ということをやる
- ワードプロセッサの例
- 文書上の画像の位置を決めて、テキストの回り込みのスタイルを指定する(specify)
- ワードプロセッサがその指定(specification)に基づいて実際に文字を並べる
234-3 (Although it may not be...)
- 一見分からないが、実はこれも同じSPECIFICATIONのコンセプトの一種
- まだ存在していないオブジェクトの条件を規定している
- ただし、実装は選別条件や妥当性検証とは全く異なるものになる
- SPECIFICATIONにマッチするオブジェクトを1から作り上げる作業
234-4 (Without using SPECIFICATION, ...)
- SPECIFICATIONを使わなくても、単に手続き的なジェネレータを作ることもできる
234-5 (Instead, an interface of...)
- 代わりに、SPECIFICATIONベースのインタフェースをもつジェネレータによって、生成プロダクトを明示的に規定するようにする
- こちらのアプローチには以下の利点がある
- ジェネレータの実装をそのインタフェースと分離できる
- そのインタフェースから生成ルールを明示的に読み取ることができる
- インタフェースが柔軟になり、クライアント側で多くの制御が可能
- テストが容易、なぜなら生成に使ったSPECIFICATIONはそのまま妥当性検証にも使えるから
235-1 (Building to order can...)
- オブジェクトをスクラッチで生成する場合だけでなく、既存オブジェクトを再度設定し直す用途にも使える
担当者のつぶやき †
- 個人的には、SPECIFICATIONパターンがDDDの中で一番熱いパターン。hamcrestやDroolsなどを使って、このパターンをフレームワーク化できないだろうか?
みんなの突っ込み †