MVCにおけるcontrollerクラスの役割は時代と共に変わって行く

昔、JavaのフレームワークであるStrutsも出てくる前、MVCモデルにおけるControllerの役割というのは、

「ロジックもデータも見ない現場監督のような役割」

と学んだ。だから昔、ServletではMVCアーキテクチャを学んだ時に、こんなControllerを書いていた。

[とりあえずRequestオブジェクトを受け取る]
  |
[validationロジックに引き渡す。データの中身は見ない]
  |
[例外が発生したらエラーView処理クラスに引き渡す。何のエラーかは細かく知らない]
  |
[次にロジック処理クラスに渡す。最終的にDBのテーブルとマッピングしたデータはJavaBeansというデータクラスが保持する]
  |
[例外が発生したらエラーView処理クラスに引き渡す。何のエラーかは細かく知らない]
  |
[Viewの生成オブジェクトにJavaBeansを渡す]
  |
[Viewオブジェクトでは、画面表示用にデータを加工し、テンプレートエンジンでレンダリング(JSP)]
  |
[Viewの処理結果をServletコンテナに返して終了。何をレンダリングしてるかは知らない]

コントローラはViewやModel、ロジックを適切にインスタンス化し、そのデータを取り回して、URLを制御してあげるだけの役割だった。
状況によって別画面にリダイレクトしたり、エラー画面を表示したり。
コントローラしか画面を出力したりURLをリダイレクトさせる機能を持っていないので、そういう役割に徹する。

Strutsが出てきてコントローラの役割範囲がActionServlet、ActionFormやActionクラスの3つに定義づけられて、ロジックはActionクラスに記述するようになった。

ところがこの頃のcontrollerの問題は、「ほとんどのWebページが、同じフローになるのでつまらない」ということだった。「リクエスト-ロジック実行(validationとDB読み書き)-view生成」しかしないのに違うのはクラスの名前だけというファイルが山のように存在し、コントローラにほとんど存在意義がないこともしばしば。

だったら整理して自動化しちゃえばいいじゃんというのは自然な発想だろう。

更に言えば、Webは「画面しかない」ので、ViewとModelで明確に切り分けがあった筈の「ビジネスロジックと画面に出力するデータの形は違うハズ」という区分けは、ほとんどのWebでオーバースペックだった。

例えば、裏で基幹システムと連携しているModelデータと、出力用文字列の相互変換処理があったならば、その処理をテンプレート側で変換するのは無駄だ。だからケースバイケースで事前処理をするのがViewのクラスの役割だった(と思ってる)。あと単純にテンプレートエンジンの呼び出し処理も書かないといけなかったしね。

しかし、そもそもWebオンリーのシステムではそういう区分けは無い。かくしてModelはそのままViewでも通用するし、せいぜいフォーマットの変換をテンプレートエンジンの中で書いてしまってもよくなっている。テンプレートエンジンの呼び出し処理はフレームワークに内蔵され、テンプレートからプラグイン的に文字列加工用のヘルパーメソッドを呼び出すスタイルが定着し、View専用クラスの存在意義はなくなった。

Railsからなんでしょうか、controllerの処理はroutingとフレームワークの中に埋め込まれ、Viewの加工クラスは、優秀なテンプレートエンジンのヘルパーメソッドとcontorllerに記述するスタイルに置き換えられた。viewの歴史はRailsの前にもいろいろありますね。JSP commonsとかsmartyとか。あと、controllerについては、そもそもWebはURLで機能の取り回しをしてくれているので、コントローラ間で連携することもなく、Webにおいてはcontrollerの役割だった現場監督はroutingのスクリプトで簡素化してしまっても十分作れるということがわかった。

Web開発の主たるロジックであるDBの読み書きもActiveRecordのライブラリとしてフレームワーク上、透過なものになってしまった。結果として、我々がフレームワークを使う場合は、フレームワーク自体がMVCモデルで作られていて、開発者はロジックの部分を自由にカスタマイズすることで、所望のWebサイトを構築するというのが、フレームワークを使った開発の考え方になる。

だから作り込まれたMVCフレームワークの場合は、見えない部分に重要な処理が委譲されているので、見通しが悪いと学習コストは高いし、何が行われているのかがわからないと怖い人には、なんとなくフレームワークを頼るのは怖いという風になる。結果として、柔軟で見通しのよさげなフレームワークを好む人が多いということになるのだろう。例えば僕は、僕の学習量の問題でrailsは、まだちと怖い。最初使った時、productionにした時にだけエラーが出て画面表示されないとか、キャッシュの問題だったと思うけど、慣れてないと怖すぎるよね。

現在のコントローラと呼ばれるオブジェクトは、自分たちのウェブサイトを構築するための「ロジックを記述する場所」になっているだけで、そもそも論として、今のコントローラをコントローラと呼ぶのは正直言って抵抗がある。

ファットコントローラ、ファットモデルという話については、そもそも書かなくてはいけなかった処理がどんどんフレームワークに組み込まれていったが故に、自分たちのロジックを、どこに書けばよいかという納まりが悪くなったのが今なのかもしれないね。しいて言えば、残った雑用が全部押し付けられたWebディレクターのような存在になってるのが今のファットコントローラ問題に符合します。

でも、DBにアクセスするコードが1行で安全に書けるなら、一々、モデルに切り出さず、直接SQLを書いてしまえば良いじゃんという気持ちは正しいと思います。それはフレームワーク側でSQLインジェクションのエスケープ処理やオブジェクトマッピングをしてくれてるシアワセがあるからこそできていることなわけです。

なので、あとの切り分けは「どうプログラムを整理したいか?!」であって、もはやMVCのかくあるべし、の話ではないと思う。

プログラムを整理する動機は「再利用性」と「テスト可能性」につきる。何故、そのプログラムがファットコントローラでも良いのか?!というと、それが事実上ユニークな処理だからに他ならない。その画面でしかそのプログラムが使えないのであれば、ロジックとしてマネージャークラスに追い出す必要も無ければ、モデルとして切り出す必要も無い。

そのユニーク性を決定づけるのがフレームワークの完成度ということであれば、コントローラに書くべき範囲はフレームワークの進化と共に変わって行く。

あとはプログラムが読みやすいか、読みにくいかという話だが、乱暴に言えば開発者の短期記憶力とソースコードの見通しの悪さとのトレードオフとも言える。個人の主観に委ねるのもどうかと思うが、逆に潔癖的に構造化するはするで、一つのプログラムは短くなるものの、クラス間の関係性がきれいに切り分けられてないと、作った人はいろいろ整理できて気持ちが良いかもしれないが、後から見る人は大変だということも多々ある。多少、冗長でも、ちゃんとソースコードで説明されてることは大切なことだと思う。

なお最近、PHPフレームワークのlaravel4をいじっているが、他のフレームワークでも常識かもしれないが、最近のフレームワークは面白くて、routeにviewやコントローラに相当するものを記述することもできる。

route.phpにこんなプログラムを書く事もできる。実際はこんなの書かないけど、書ける。

$logic = function(){
return “Hello world!!”;
};
Route::get(“/my/hoge”,$logic);

ブラウザで/my/hogeにアクセスすれば画面に「Hello world!!」が表示される。

最短で書くなら、

Route::get(“/my/hoge” , “Hello World”);

で良い。つまりcontrollerクラスかView、役割が違うクラスだが、どっちでも良いのでroutingに文字列を返せば、画面に文字が表示される。

もはやMVCでviewもcontrollerもへったくれもなくて、「そういうもの」なんだと思う。ロジックを書きたければcontrollerクラスを使えたまえ、でもView直接でも良いから、よしなに値を返したまえ!、と。

viewとcontrollerの関係性がうまく繋がっていて、すごく素敵だ。何よりPHPっぽくてスピード感ある。

特徴としては例えば、単純にリダイレクトだけをしたいURLができてしまった場合は文字列ではなくて、Redirectオブジェクトを返してあげる。

Route::get(“/my/hoge2”,Redirect::to(“なにがしかのURL”));

これで、第一引数に書かれたURLはリダイレクト専用になる。キャンペーンページを作ったり取り下げたりするような運用をしているサービスにとってはとても利便性があがることだろう。また、モックアップを作るのも楽だね。コマンドラインを使わずにモックを足して行けるのはすごく楽しい。

余談にはなるが、コマンドラインインターフェースもあるが使う必要性を感じないほどシンプルでよくできてる。RESTのapiを定義づけるのも簡単で、

routingに、

Route::controller(“/myapi”);

って書いておくと、あとはコントローラに

MyapiController.php

public function getUserid(){};
public function postRegister(){};
 ・
 ・
 ・

とメソッドを「手書き」で、ぽいぽい足して行く事で、GETの/mypapi/userid apiや POSTの/myapi/registerなどとapiを足して行くことができる。あとは中身を実装すればOK。とにかくどんどん動くものを定義しちゃって、実装を頑張ろう!先に動くものを定義しちゃって、実装を埋めて行くアプローチが一番力が出ると思う。
 
今のWeb MVCモデルの流れを決定づけた元祖とも言えるベストセラーStrutsは、僕は仰々しくて苦手だったので、こっちの方が圧倒的にシンプルで好きだな。

素敵なフレームワークに従うというアプローチを取っている限り、MVC間の連携をどうデザインし、どういう裁量が与えられるかはフレームワークの設計思想に準拠する。それに従って、controllerと言う名前がついたクラスの役割も広くなったり狭くなったり、そもそも書く必要がなくなったり、ということになっているのは、MVCがフレームワークを訴求するパターンになっているから、ということであり、乱暴に言えばフレームワークを使う側にとってのcontrollerクラスというものが形骸化しているとも言える。

実は本当は新しい構造になってるのに、みんなに使ってもらうために必要だからcontrollerって言ってるというのはあるのかもしれないね。そんな遠くないうちに、「○○でいいじゃん」とMVCよりもシンプルな呼び方に原点回帰していくような気もするが、すでに起こってる気もするなぁ。

【PR】BASE株式会社 17職種、仲間を絶賛募集中!