Skinny Controller, Fat Model ?

コントローラーががんばりすぎる設計(Fat Stupid Ugly Controllers)に対するアンチテーゼとして出てきた話、"Skinny Controller, Fat Model"

元ネタ

元はこのブログ記事Buckblog: Skinny Controller, Fat Modelらしいですが、元ネタの元はこれ?http://www.amazon.com/Fat-Stupid-Ugly-Courage-Survive/dp/0757302254
さらに、http://www.survivethedeepend.com/zendframeworkbook/en/1.0/the.model#zfbook.the.model.the.fat.stupid.ugly.controllerここでも語られていました。
それが推奨される理由として、

FSUCに傾倒する理由として、コントローラーをゲートウエイにしてアクセス制御を行うという目的がありますが、コントローラーをデータに対するアクセス制御に利用した場合、ビューヘルパー等でモデルを直接参照された場合などに制御できない。
(超意訳)

http://www.survivethedeepend.com/zendframeworkbook/en/1.0/the.model#zfbook.the.model.controllers.are.not.the.data.police

とか、モデルの中にビジネスロジックのすべてを内包するようにしておけば、Zend Frameworkを使っていないシステムにも容易に移植できるよね。ということ、いくつか例に上げたうえで、FSUCよりもSkinny Controller, Fat Model がいいよと力説していただいています。

そもそもFSUCって

MVC実装の基本例としてあげられるパターンとしては、コントローラーがモデルからデータを取得してビューにアサインして出力するというものでした。そこではデータ取得前後の「制御」はコントローラーの仕事となり、モデルをデータアダプタとして扱い、「ビジネスロジック」を「制御」側に持たせるという流れが生まれます。
ビジネスロジックが軽量な場合、それでもそれほど問題は起きないと思いますが、複雑なドメインロジックを持っているシステムの場合、コントローラーでの処理が肥大化してしまい、モデルとの癒着が酷くなります。成分無調整で、リクエストを解析して処理ロジックまですべてコントローラーに実装してしまったら、それは酷いことになります。
その状態のことを、"Fat Stupid Ugly Controllers"と呼んでいるようです。
そもそもシステムをMVCに分離して考える第1歩は、VCに対してMを分離する必要がありますが、それが果たされなくなってしまうわけです。

Skinny Controller, Fat Model、それ、おいしいの?

肥大化して頑張りすぎたコントローラーは"Fat Stupid Ugly Controllers"と揶揄されてしまうわけですが、それに対してコントローラーを軽量に保ってロジックをモデルに収納してしまう"Skinny Controller, Fat Model"は本当にうまいんでしょうか。
ビジネスロジックのすべてをモデル側に隠ぺいするということは、モデル側に制御機能の多くを実装することになり、Controllerに対してAPIを提供するようなシステムになります。それじゃぁ、そのAPIの実装に制御をすべて含めることができるのでしょうか(アクセス制御とかビジネスフローとか)。そんなことができるなら、なんのためのFWかわかりませんね。というかFWいらないでしょう。結局、モデルの中にもう一つMVCを作るようなものです。それは結局、システムをフレームワークに載せようとしたが、うまくいかないのでフレームワークからアクセスさせる別システムを作りました、と言っているようなものです。
Fat Contollerがダメだというのと同じ理由で、モデルもFatであってよいわけはないんです。

dispatchLoopをうまく使おうよ

Zend FrameworkではDispatcherがリクエストをコントローラーに割り当てます。最初のリクエストをきっかけにして、複数の処理に分割しマップしてあげることで分散処理に近い粒度を実現できます。これ、システム構築時は非常に重要な要素です。シーケンシャルに記述されそうな機能を、必要な処理を解析して分割し、コントローラーにマップして、最終的に成果物を合わせて出力していくという流れにすることにより、各作業単位を明確にした動作テンプレートを提供してくれているわけです。
Fat Controllerの欠点は、1コントローラーでオールインワンで処理しようとするところに主原因があるので、適切な粒度にコントローラーを分解して実装すれば何の問題もありません。コントローラーの中には、リクエストを受け入れてコマンドパターンを励起するものもあるでしょうし、ビジネスロジックのプロシージャー的に実装しておいて、それらのリクエストを受け入れたコントローラーから処理を引き継ぐものもあるでしょう。バッチ処理用のコントローラーは何をバッチするかを受け取って、コントローラーへのマップだけを作成するとか、適切にそれらの役割を果たせばよいわけです。
それら一連の処理がすべてアクションコントローラーのテンプレートメソッドパターンに収納されることにより見通しのよいシステムを作ることができます。作業単位の粒度を揃えて、各単位の始まりと終わりを同じ作法で処理し、ある処理が他の処理に依存しないように書きあげる必要があります。そうすればバグを軽減するだけではなく、キャッシュしやすく、場合によっては分散処理への布石とすることもできます。横断的な処理をしやすくモジュール内の規約を適切に適用できます。

つまり、悪いのは、

FSUC*1が生まれてしまった理由はコントローラーにビジネスロジックを搭載したことにあるのではなく、コントローラーでの粒度調整の失敗にあるのです。そして、この粒度調整はFat Modelに対しても適用するべき法則の一つとなると思います。つまり、ビジネスロジックをコントローラーに実装しようが、モデルに実装しようが、やるべきコード調整は同じです。ですので、FSUCを嫌って、"Skinny Controller, Fat Model"に逃げた実装は、目の前の問題を隠蔽するだけで本質的な解決にはなっていないリスクを負っていると思います。
解決するとしたら、上記したような粒度調整やディスパッチの仕組みをモデル側にも実装するんでしょうか。なんとなく無駄感が否めません。
もし、"Skinny Controller, Fat Model"がWeb用MVC実装のデファクト的になっていくとしたらそれは残念な感じがします。
どこに何を実装するとか、そんなのどうでもいいんです。
M - VC という分離が基本とはいっても、M寄りのCもあるので、それをアプリケーションレイヤーとよぶかサービスレイヤーと呼ぶか、そういうのは好きにしたらよくて、うまい方法で実装してあればいいんじゃねってことかと。その意味、コントローラーでビジネスロジックを実装したら、Fat Stupid Ugly Controllersなんて呼ぶのはどうかなと。たぶんWhat とfatのごろ合わせだと思うんですよね。FSUCなんて揶揄するセンスじゃぁ、そりゃFat Modelになるさと。今日言いたかったのはそんな愚痴かもしれない。

まとめ

FSUCを解消してdispatchLoopによる適切な粒度調整と横断的処理を実装したZFアプリを作るなら

  • コントローラーでの実装の目的を明確にする
  • 処理の粒度を揃える
  • 内部コントローラーに外部からルーティングされないように工夫しておく

ってことぐらいですかね。

*1:Fat Stupid Ugly Controllers