See the Elephant

1992生まれのプログラマが書くエンジニアブログ

【SQLアンチパターン】24章 マジックビーンズ

SQLアンチパターン】24章 マジックビーン

twadaさんが弊社でSQLアンチパターン勉強会をやってくださっている。

聴講メモと自分なりの解釈のまとめ。

マジックビーンズはMVCにおけるModelレイヤの振る舞いについて触れた章。

Active Recordパターンの扱い方について議論している。

結論

Active Recordパターンを使うときもビジネスロジックとDBの中間層(Repository層やドメインモデル層)を設けてあげることで辛さが薄くなる。

Active Recordパターンが登場する文脈

弊社エンジニアのブログも話の中で登場したので記載する: http://at-grandpa.hatenablog.jp/entry/2013/11/01/072636

ソフトウェアプロジェクトの最大のコスト要因は開発工数工数削減 = 開発コスト削減

重複部分をいかに早く作るか。つまりボイラープレートがあればいい。

再利用性を高めることで開発の生産性を改善できる。

webアプリの主流アーキテクチャ MVCRailsの関係性

Active Recordパターンの流行はRailsが立役者。

webアプリにおいて、もっとも有力なアーキテクチャMVC

なかでも、モデル(Model) はコントローラ、ビュー以外の全ての部分を担う補集合的な立場。

ビジネスロジックのほとんどを担う部分である。

つまり、Modelを単純化することができれば、アプリケーションそのものの開発コストも削減できる、という考え方が生まれた。

それをActive Record パターンで実現したのがRailsの思想。

active recordパターン

1. モデルオブジェクトのフィールドがテーブルの列と1:1対応する
2. モデルオブジェクトがテーブルへのcrud操作を知っている
3. モデル特有のビジネスロジックをもつ

これがアクティブレコードパターン。

MVCの役割

at-grandpaさんのブログから拝借

f:id:namu_r21:20190724192017p:plain

現在webアプリケーションではMVC2の方が主流。

  1. ユーザからの入力を受け取る
  2. コントローラがモデルを呼び出す
  3. モデルの状態を取得
  4. 取り出したモデルの状態をcontrollerが加工
  5. viewとして整形しuserに返す

元々は元祖MVCがオリジナルの考え方だった。

元祖の場合、ユーザからの入力(例えば文字入力)ごとにdbへのioが走り状態が変化しviewはそれを写像するだけ、という設計だった。

ネットワークを介してこの操作を行うことはいささか富豪的すぎたため取り入れられずMVC2の形が主流となった。

元祖MVCの考え方は現代のフロントサイドFWが取り入れている(fluxのような思想)

MVCにおけるModelの役割は曖昧

MVCにおいてVとCは役割が明白だが、モデルは役割が曖昧である。

開発者はソフトウェア設計の複雑さを低減するためにモデルの一般化を行いたい。

しかし、モデルを過度に単純化し、ただのデータアクセスオブジェクト(DAO)とみなしてしまうのが今回のアンチパターン

何が辛いのか

データベーススキーマ変更の影響をモロに受ける

ActiveRecordだとテーブルとクラス(モデルと呼ばれる)が1:1対応する。

モデルはテーブルと密結合なのでテーブルの変更が直で影響する。

ドメインモデル貧血症

モデルがビジネスロジックを持たない単なるデータオブジェクトとして扱うと、ビジネスロジックが複数のコントローラへ漏れ出してしまう。

つまりモデルの振る舞いの凝集度が下がる。

これが起きると何が辛いか。

1モデルのロジックが各所に点在してしまいメンテナンスがむずかしくなる。

そして、 テーブル変更がモデルへ影響を与え、モデルを利用しているコントローラのビジネスロジックにまで影響を与えてしまう のだ。

つまり、永続化層のリファクタリングをやりたいだけなのになぜかコントローラのレイヤまで影響を与えてしまいとても辛くなる。

モデルのテストが難しい

モデルがDBアクセスとビジネスロジック両方を保つため、ビジネスロジックのテストにDBが必要となる。

データベースへのIOを考慮したテストを組むことになりテスト困難になる。

しかも、コントローラにビジネスロジックが漏れ出していた場合、あるメソッドのテストをしたいだけなのにhttpを利用したE2Eテストが必要になる。

どの部分が問題を産みやすいのか見つけづらい上に、考慮すべき範囲が広い。

辛いことばかりに見える。

ではどうすればいいのか

railsにおけるモデルはデータアクセスオブジェクトとして機能する。

そこにビジネスロジックも乗っかっているような形になっている。

データアクセスの機能とビジネスロジックの機能を切り分けて考えればいい。

データアクセスはいわゆるRepository層的な役割として見立てモデルに任せる。

そしてビジネスロジックはモデルではない副作用を持たない別の純粋なクラスに置く(DDDのドメインモデル的な役割)。

こうすることでアプリケーションのビジネスロジックの関心事と永続化層の関心事を切り分けられる。

結論

Active Recordパターンを使うときもビジネスロジックとDBの中間層(Repository層やドメインモデル層)を設けてあげることで辛さが薄くなる。