Magic Beans
Decouple your model from your tables.
-
フレームワークのコードと密結合してテストがままならない
- DBをリロードしなければならない
- 1つの機能を追加するだけなのに甚大な工数がかかる
Objective: Simplify Models in MVC
-
MVC
-
Controller
- ユーザ入力を受け取る
- レスポンスを返却する
- 適切なモデルに処理を委譲する
-
Model
-
他すべてを司る
- 入力のバリデーション
- ビジネスロジックの解決
- DB操作
-
-
View
- UI上で情報を表示
-
- VとCはわかりやすい
- Mは曖昧
- ソフトウェア設計の複雑性を減らすために、「モデルとは何たるか」を一般化・単純化したいという強い欲求がある
-
「データアクセスオブジェクトにすぎない」という過度な単純化に陥る
- DAO: Data Access Objects
Antipattern: the Model Is an Active Record
-
単純なアプリケーションでは
- モデルに多くのカスタムロジックを積む必要がない
- 単一のテーブルのカラムとモデルオブジェクトとが素直に一致する
-
PoEAAのActive Record
- 【補】ロジック積まないならRow Data Gatewayじゃない?
- RoRが有名にした
- DAO
-
すべてのモデルをActive Recordにしてしまうのはアンチパターン
- 【補】原典のPoEAAでも「複雑なドメインロジックには対応できない」と言っている
- 金槌しか持っていないと、すべてのものが釘に見えてしまう
-
ジャックと豆の木
-
CoCへの批判
- ジャックは寝ている間に豆の木が天高く育つことを知っている
- ジャックの物語ではうまく働く
- しかし他の物語でもいつもうまくいくとは限らない
-
コラム: Leaky Abstractions
- オブジェクトでデータ永続化を隠蔽・汎化しようとする
- しかし、JOINとかGROUP BYとか、結局RDBを意識しないといけない
Active Record Couples Models to the Schema
- そのまんま
Active Record Exposes CRUD Functions
-
ビジネスルールをクライアントコードに強制できない
- Active Recordを継承すると、親の
find()
やsave()
はクライアントコードに見えてしまう - 「レコード保存時にメール送信する」ビジネスロジックを実現する
assignUser()
メソッドを作ったとする save()
生呼びされると、メール送信が行われない
- Active Recordを継承すると、親の
Active Record Encourages an Anemic Domain Model
- 「貧血症」ってやつ
-
DAOがドメインロジックを持たないため、クライアントコード側にドメインロジックが漏出する
- Controllerとか
Unit Testing Magic Beans Is Hard
-
MVC各層のテストが困難に
-
Model
- DBから切り離してテストできない
- DBのfixtureは遅くエラーが生じやすい
-
View
- 同様
-
Controller
- Modelが単なるDAOだと、ドメインロジックがControllerに漏出
-
多くのControllerでコード重複が生ずる
- ので、テストも重複する
-
ドメインロジックをテストするためにHTTPリクエストをモックしないといけない
- セットアップコードが大きくなる
- 遅い
-
How to Recognize the Antipattern
-
こんなのが聞こえてきたら、Magic Beansアンチパターンの証
-
「モデルにSQLクエリを渡すには?」
- 「モデル」を「DBアクセスクラス」の意で使っている
- データアクセスを隠蔽できてない
-
「複雑なモデル問い合わせをすべてのコントローラにコピペすべきか、1つの抽象コントローラに持っていくべきか」
- どっちもNO
-
モデルに閉じ込めろ
- DRY原則
- モデル利用方法がシンプルに
-
Legitimate Uses of the Antipattern
-
シンプルなCRUDならどうぞ
- Active Recordパターンそれ自体はわるくない
-
プロトタイプ開発
- 開発速い
-
「プロトタイプモード」で書いたコードは技術的負債であることに留意せよ
- リファクタリングの工数は確保せよ
Solution: the Model Has an Active Record
- 継承ではなく集約・委譲
Grasping the Model
Information Expert
- ある操作が複数DBにまたがるような場合
- Active Record (単一DBに対応)を複数集約するクラスを用意せよ
Creator
-
DAOを集約するクラスがDAOを構築するように
-
【補】今日びはサービスコンテナから取り出したりする
- PoEAAのRegistryが近い
-
Low Coupling
- アプリケーションの要件それ自体は単純化できない
- どこかは複雑にならざるを得ない
- 複雑な部分を疎に切り離すことが肝要
- 【所感】関数型プログラミングで「副作用」を隔離するのと似た考え
High Cohesion
-
ModelとDAOとを切り離す利点
-
assignUser()
のようなメソッドを提供できる- Controllerから利用したいのはこういうメソッド
find()
やsave()
といったメソッドはアプリケーションの要件を表していない
-
複数のモデルから同一のDAOを利用できる
- テーブル操作単位でModelをまとめるよりも凝集度において優れる
-
Putting the Domain Model into Action
-
Domain ModelにDAOを集約させる効能
-
クラスの関連がすっきりする
- 凝集度が高まったため
-
ModelのインタフェースとDB操作とを切り離すことで、クライアントコード(Controller)がすっきりする
- Controllerからは、「ある操作がどのテーブルが関わっているか」意識しない
- DBクエリも意識しない
-
Active Record (ORM)で記述できない複雑なSQLクエリが必要になったら、Model内に閉じることができる
- Controllerにまで漏出しない
-
Testing Plain Objects
-
Model
- DAOのstubを注入し、DBと切り離してテストできる
- ドメインロジックをControllerから切り離したため、HTTPリクエストのモックなしでテストできる
-
Controller
- HTTPリクエストのモックは必要
- だが、Controller自体薄くなったためテストは楽
Getting Down to Earth
- 本章の内容は基礎
-
オブジェクト指向設計を勉強しろ
- でないと結局スパゲッティコードになる
英語
-
per se
- それ自体に