PHPerのMVCの一体どこが間違っていたのか

メリークリスマス! PHP Advent Calendarもいよいよ24日目に突入です。
昨日はxhprofについてでしたね。僕もパフォーマンスチューニングの際に使っています。手軽に利用できるのでお勧めです。

さて、このエントリーでは表題の通りMVCについて書かせていただきます。これは、PHPカンファレンス2012&WordCamp Tokyo2012合同LT大会で発表した「やはりお前らのMVCは間違っている」で煽るだけだったこの問題をきちんと解説するものです。
この発表資料を公開するとPHPの枠を超えて広く閲覧いただき*1、また多くの方から突っ込みを戴きました。「LTだから」と言って逃げていた回答をして、気持ち新たに新年を迎えようと思います。

MVCとはなんなのか

間違いを指摘する前にMVCがそもそもどういうアーキテクチャであるのかを確認しなければいけません。

MVCは1970年代にパロアルト研究所(Xerox Parc)で発明され、1980年代にSmalltalk-80のドキュメント*2に登場します。このMVCのコンセプトはGUIアプリケーションにおいてオブジェクトを3つに分類しました。

Model
ドメイン領域のデータと振る舞い
View
ユーザーに対する視覚表現
Controller
ユーザーの操作を受け取りModelやViewに命令を送る

また、これを実現するため下図に示したオブザーバーパターンによる連携を用いました。

これこそがMVCの原点です。

@koichikさんからの指摘に基づき上図を変更しました。後に下図のような連携も出てきますが、原点で示すべきは上図でした。訂正してお詫びします。


Webへの対応

WebへのMVCの導入はJAVA界隈で行われました。この時、HTTPがステートレスであることに対応してMVCに変更を加えMVC2としました*3 *4。すなわち、MV間、VC間のオブザーバーパターンによる通知を排除し下図のような構成にしました。

このようにMVC2でもそれぞれの役割分担がはっきりしており、またデータの流れも均一です。

ではStrutsの実装を見てみましょう。StrutsMVC実装は下図で示したものとなります。ViewがJSP(テンプレート)でActionとActionForm BeanがModel領域とフレームワークとの間に立つアダプターパターンを採用しています。ViewがJSPになった背景にはデザイナーの範囲を分離する意図があったようです。*5

PHPでもMVC2実装がなされ、Mojaviとして公開されました。Strutsやその後に登場した多くのWebApplicationFramework(WAF)でViewをテンプレートのみで構成ているのに対して、MojaviではViewをテンプレートとViewクラスの2つで構成しているのが特徴です。これにより、複雑なプレゼンテーションロジックをテンプレートから排除しながら、コントローラの肥大化を避けています。(裏を返せば、Viewをテンプレートのみで構成するとプレゼンテーションロジックが行き場を失います。)

Ruby on Railsの登場

Ruby on RailsがWAFに与えたインパクトは絶大でした。 フルスタックであり、自動コード生成機能、テスト機能そしてActiveRecordなど、その思想は後続の多くのWAFに影響を与えています。

RailsにおいてModelを生成するには次のコマンドをたたくだけでした。

rails generate model profile

たったこれだけでプロフィールクラスのひな形が出来上がります。そして出来上がったひな形はActiveRecordを継承しています。これがMVC初心者に誤解を与えることになりました。

RoRのコマンドで生成されるモデルはドメインモデルで言うところのエンティティに当たるものです。これはMVCのモデルの部分集合にすぎません。しかし、"モデル生成コマンド"で生成されるものだけがモデルであると人々に誤解を与えるのに十分でした。*6

その結果、本来モデルに実装されるべきビジネスロジックが行き場を失いコントローラへ集まってしまいました。こうして憎むべきファットコントローラが量産されるようになります。

この波はPHPにもCakePHPとして訪れます。CakePHPはまさしくRoRのクローンであり、自動コード生成によりActiveRecordなモデルを生成しました。しかも、そのクラスの基底クラスはAppModelという名前でした。結果として多くのPHPerがMVCを誤認識することになりました。

PHPerの認識しているMVC

さて、このようにして多くのPHPerが認識するMVCが誕生しました。この特徴を一度整理してみましょう。*7

Model
自動生成されたアクティブレコードのモデル
View
テンプレートファイル。ロジックは排除。モデルへのアクセスは厳禁。
Controller
Model,View以外のすべてをコントロール

またMVC間の関連を下図に示しました。

このアーキテクチャでは必然的にコントローラは肥大化します。

何が間違っているのか

ここまでの説明でPHPerの認識するMVCSmalltalk MVCの違いを理解いただけたかと思います。
件のLT発表において私が「間違っている」と断言したのは、この違いにより冒頭で紹介したMVCのコンセプトから外れてしまっているからにほかなりません。コンセプトがズレているのにそれをMVCと呼び続けることは厚顔無恥と言わざるをえません。
しかし、MVCから外れることそのものは悪ではありません。MVCとて完璧なアーキテクチャではないですし、そもそもがGUIアプリケーション用のアーキテクチャなのでWebアプリケーションには適さない(あるいはオーバースペックな)部分もあります。
そして、もしMVCに確固たる意志をもってコンセプトから外れるような手を加えてるのであれば、そのアーキテクチャに新しい名前をつけるのが良いかと思います。

結論

PHPerの認識するMVCがどのように出来上がったのかを振り返りました。MVCのコンセプトからのずれが生じた背景には何らかの問題を解決する意思があったのだと思います。しかし、そのずれは次第に周りに影響を与え結果として多くの人が全く別物をMVCとして認識するに至りました。時には立ち止まり、振り返ることが大切なんだと痛感させられます。
2012年もあと少しです。私たちも今年を振り返って小さな間違いのうちに改めていきましょう。

PHP Advent Calendar、明日はいよいよ25日目、ジェネレータが実装されるPHP5.5に備えてforeachについてのお話です。

*1:まさかの3万views越え!

*2:ネット上には原典は見つかりませんでしたので後継ドキュメントを示します "Applications Programming in Smalltalk-80(TM):How to use Model-View-Controller (MVC)" by Steve Burbeck, Ph.D. http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html

*3:MVC2という用語はJSFMVC対応Type2を指すものがあれこれした結果であるとかないとか?要出典

*4:こちらの"Model2" architectureが元のようです http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-eedocs-419425.html#7036-design_ent_app-2.0-oth-JPR

*5:参考: http://struts.apache.org/1.x/index.html

*6:参考: Life is beautiful: Ruby on Railsの「えせMVC」の弊害

*7:あえて、一番ダメな例を出します