SQL Antipatterns ch25 Magic Beans

SQL勉強メモ

出典: 


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 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

    • それ自体に