先日のエントリーでも少し触れたが、Ruby on Railsの最大の問題点は、それが持つ「一見そのフレームワークがMVCの形をとりながら、MVCの最も大切なところを外している『えせMVC』である」点にある。MVC(Model View Controller)がなぜ必要かを根底の部分でちゃんとと意識せずにRailsアプリケーションを作ると、後々ひどい目に会うので注意が必要である。
その意味では「RailsでMVCを学ぶ」などもっての他だし、「JavaにもRailsと同じようなフレームワークを作って業務用アプリの開発を効率化しよう」などという発想もとても危険である。
ということで、今日はまずはMVCの解説から。
MVCの発想の根底には、「モジュール化と情報の隠蔽により、プログラムがスパゲッティ化するの(コード間の相互依存関係が複雑に入り込んでしまってにっちもさっちも行かない状態になること)を避けよう」というオブジェクト指向の発想がある。MVCは、そのオブジェクト指向に基づいて、GUIアプリケーションを以下の三つの大きなモジュールに分けて設計しましょう、というアーキテクチャの指針である。
- Model:データベースなどに格納された生のデータを隠蔽し、抽象化された形のAPIを通じて他のモジュールからのアクセスをコントロールするモジュール。アプリケーション特有のルールやロジック(ビジネスロジック)を持ち、データの整合性(後述)に絶対の責任を持つ。
- View:Modelが提供する抽象化されたデータを、どんな形で人間に見せるかを記述したモジュール。GUIアプリケーションの場合、使うGUI部品や画面上のレイアウトを指定するのがこのモジュール。
- Controller:ViewとModelの間に位置して、ユーザーにどんな順序でデータを見せて行くとか、Viewを通したユーザーからの入力をModelへのAPIコールへとマッピングするのがこのモジュールの役目。
この中で、もっとも丁寧に設計し徹底的にテストをしなければならないのがアプリケーションの土台となるModelである。ControllerやViewに多少のバグがあってもユーザーに一時的な迷惑をかけるだけの話だが、Modelにバグがあるとデータの整合性が壊れてしまう。
ここで、先ほどから何度か触れている「データの整合性」とは何かを、業務用の「会計ソフト」を例に上げて説明してみよう。例えば、八百屋さんで「60円で仕入れたリンゴ1つを100円で売った」こと(Sales Transaction)を記録する場合を想定しよう。
会計ソフトが一般的な会計の常識に従って作られていれば、この1つのSales Transactionにより、少なくとも4つの変更がデータベースに加えられる。
- 「手持ちの現金の増減」を記録するテーブルに「現金100円の増加」を記録
- 「売り上げ」を記録するテーブルに「100円の売り上げ」を記録
- 「在庫の増減」を記録するテーブルに「リンゴ1つ減少」を記録
- 「経費の計上」を記録するテーブルに「仕入れ値60円の経費計上」を記録
八百屋さんのビジネスにとって大切なことは、この4つの変更が同時に行われること。このうち一つの変更だけが欠けていたりすると、テーブル間のつじつまが合わなくなってしまい、年度末の決算がちゃんとできなくなってしまう。
この「データの変更が中途半端でつじつまが会わなくなってしまっている状況」のことがすなわち「データの整合性が壊れた」状況である。
Modelの外部インターフェイスの設計においてもっとも大切なことは、この「データの整合性」の責任を100%Model側で引き受け、「Controllerが何をしてもデータの整合性だけは絶対に壊れない」ように作っておくことである。そのためには、上の例の「手持ちの現金の増減を記録したテーブル」へのControllerによる直接のアクセスは絶対禁物である。Controllerが出来る事は「何をいくらで売ったか」をModelに報告するだけで、その情報に基づいてデータベースに適切な変更を加えるのはModelの役割である。
この「Controllerが何をしてもデータの整合性だけは絶対に壊れない」という部分はとても重要で、Controllerに少々のバグがあろうが、会計の知識が全くない新人がControllerを作ろうが、Controllerに悪意のあるコードが紛れ込もうが、整合性だけは絶対に壊れないようにModelを作っておくことが必須である。
さて、そこで問題となるのは、Ruby on RailsというフレームワークでModelの役を果たしていると言われている(もしくは誤解されている)ActiveRecordである。
RailsのActiveRecordは、データベースのテーブル(厳密にはクエリーの結果)の1行1行をオブジェクトの形にパッケージ化し、メソッドとプロパティの形で簡単にアクセスできるようにするもの(「O/Rマッピング」と呼ばれる)である。
ActiveRecordそのものはとても便利なもので全く問題はないのだが、問題はRailsの解説書などでActiveRecordを使って抽象化されたデータベースをModelと読んでいるケースが多く見受けられる点だ。
上に述べた通り、ActiveRecordは、データベースのテーブルを単にオブジェクトの形に抽象化しただけのものであり、そこにはビジネスロジックは一切含まれていない。その意味では、MVCにおけるModelとはほど遠いものであり、これをModelと呼ぶ事は(特に初心者に)大きな誤解を生むのでとても危険である。
私は「どんなプログラムを常にMVCに厳密に基づいて作るべきだ」と主張している分けでも、「Ruby on Railsは使い物にならない」と批判しているわけでもないので誤解しないでいただきたい。ここで私が強調したいのは「Ruby on Railsフレームワークは本来のMVCとはほど遠い」「Ruby on Railsの上にMVCのことを意識せずに普通にアプリケーションを作ると、ビジネスロジックをControllerに混ぜ込んで書く事になってしまい、『データの整合性』を保つことが難しくなる」という点である。
(誤解した人が多かったので追記すると)結論としては、「Railsを使って(データの整合性が大切な)アプリケーションを作る場合、素のままのActiveRecordにControllerから直接アクセスするのは避け、ActiveRecordの上に一枚皮をかぶせる形でビジネスロジックを含んだModelをきちんと設計・実装し、ビジネスロジックがControllerに浸食していくことを意識して避けることが大切である。
なお、読者からのコメントで学んだのだが、Javaにおいても同じような「えせMVC症候群」は蔓延しているようで、MVCの本質を理解していないエンジニアが、うすっぺらなDAO(Data Access Object)の上にビジネスロジックを含んだ分厚いControllerを書きながらそれをMVCと思い込んでいる、という話は良くあるらしい。
それから「MVCは時代とともに変わる」というコメントもいただいたが、この手の基本原理の定義を変えてしまうことはとても混乱を招くのでやめた方が良いと思う。MVCの発想は未来永劫いろいろな場面で使える話なので、別のアーキテクチャにはちゃんと別の名前を与えて混乱を避けるべきである。
【参考文献】