理解しやすいように適当に遮ったり、言い切ってしまったところもあるがご容赦いただきたい。
MVCの登場
MVCは、SmalltalkのGUIライブラリのモデルとして登場した。
これはGUIアプリケーションを記述する際に、適切なモデル化を進めるのにとてもいい考え方だと思われていたし、実際にそうだった。
これはアーキテクチャパターンとして、それぞれがどのように依存するべきか、どこにコードを書くべきかということを端的に表している。
安定依存の原則というものがある。これは、要件が安定しているモジュールに依存し、要件が変動しやすいモジュールには依存しないようにするという原則だ。MVCアーキテクチャでは、GUIアプリケーションの安定関係をModel > View > Controllerの順でとらえている。データ処理や業務要件というのは安定しており、UIパーツもまた比較的安定している。それらを統合してアプリケーションとしてくみ上げる部分が一番安定していないというようにとらえた。
なので、ModelはViewやControllerから依存されるが、Controllerは依存されない。その他の関係はイベントのように抽象度が高いものを使っている。
実際にはもう少し粒度の細かいデザインパターンの集合として記述されることが多い。その代表例がイベントを抽象化するObserverパターンや、インタフェースを集約するFacadeパターンだ。
このモデルは現在に至るまで長いこと支持されている。もしかしたら金科玉条のように宗教的な信仰を受けているといっても言い過ぎじゃないかもしれない。
今日は、MVCが最近どのように変化しているかという話をしたい。
#Document-Viewモデル
MVCをもとに実際にアプリケーションを書いていくと、UIとコントローラには実際的には依存関係が強いことが多い。結局のところ1つのUIのために1つのControllerというように1:1の関係があった。
そこで、1つの割り切りとしてMFC(WindowsのC++ GUIフレームワーク)ではDocument-ViewモデルというM+VCというModel層とイベントを内包したUI部品という形を採用した。
これはイベント駆動のUIと切り離されたスレッドを持つ状態を共存させるのにいい考えだった。
#ドメインモデル貧血症
一方で、MVCでの開発にはやっかいな現象が起き始めた。ドメインモデル貧血症である。
http://en.wikipedia.org/wiki/Anemic_domain_model
これは、要件追加が主に安定しないViewに近い部分から発生するために起こりやすい現象で、あるフィーチャーを開発する際にデータ処理をモデリングできず、Controller層で直接データベースを触って、必要なトランザクションまで記述してしまうというために起きた。そのためデータ処理の完全性を保証することが難しくなったり、テストの難しいコードとなってしまいバグの増加を招いた。
ドメインモデルは、業務上のロジックをすべてモデルとして隠蔽することが望まれていたが、追加要件に対してもろいことが多かった。
これは同時にO/Rマッパーの普及によるところも大きい。O/Rマッパーはデータベース処理をObject指向のライブラリに隠蔽することができるが、ドメインモデルの設計を助ける訳ではない。複数のデータベースにまたがるドメインモデルも存在するし、それがDBである必要性も本来的には無いのだから当然のことだ。
O/Rマッパーも複雑な要件に耐えるため、ActiveRecordというデータベース上のRowとDomainModel上のEntityを一致させる簡易的なものからDataMapperというデータベース処理をEntityにマッピングさせるというタイプのフレームワークなどを提供するようになってきた。
ライブラリの普及だけではなく、デザインパターンに関してもDDD(Domain Driven Design)と呼ばれるドメインモデルの分析から入る設計の必要性が説かれるようになった。
これは、ドメイン駆動による設計を実現するための共通言語を作ることが目的だった。そのなかでサービス層/サービスクラスという概念が登場した。これは問題領域そのものの概念化ではなく、インタフェース提供や粗結合化を実現するためのモジュールである。
#MMVC
これに関連して、MMVC( Model Model View Controller )という考え方が出てきた。
http://c2.com/cgi/wiki?ModelModelViewController
モデル層を2つに分割し、DomainModelとApplicationModelとよぶ。このため MAVCと呼ぶ人もいる。(DAVCと呼ぶ人はいない。)
DomainModelは状態を持つオブジェクトとして作り、複数のDomainModelを利用した処理をApplicationModelというサービス層に隠蔽する考え方だ。
このようにControllerが太る理由は、Model層についてモジュール化するためのノウハウやルールが確立されていないからだという考えのもとMVCの進歩は進んできた。
#DCIの登場
最近ではDCIという考え方が出てきた。DCIの問題意識は、Modelに次々に追加される要件に対応することだ。これはAgileな開発というスタイルにも関連している。DCIの考え方はDataよばれるデータソース層に対して、Roleと呼ばれる役割をContext上でアドホックに組み込んで提供するものだ。(なぜ DRC:Data Role Contextじゃないのかはよくわからない。)
RoleはTraitで実装される。とても現代的なものではあるが、有効性や問題点はまだ未知数といってもいいだろう。
このようにControllerにModelで書くべきことが記述される問題に関してはアプローチされてきた。ではControllerとViewの関係はどうであろうか。
#MVVMあるいは双方向バインディング
実際のところ、ViewとControllerの間で記述されている処理は非常に明快なことが多い。Viewへのアクションに伴うイベントのハンドリングや、データのアサインをModelを介して行うだけだ。これはModelが十分な機能を提供している限り、これ以上のことをする必要は無い。であれば、ViewとModelの関係を明快にすることでControllerの肥大化は防ぐことができる。
そこで出てきた考え方がMVVMだ。(Model View ViewModel )
UIには入力フォームや表示データのように見た目とは別のデータ構造を隠し持っていることがわかる。そのUIパーツのためにアサインしているデータを考えればわかりやすいだろう。
このデータ構造をViewModelといい、その変更を監視しモデルに伝え、モデルがViewModelを変更するといったインタフェースを持つことで煩雑なイベント処理を隠蔽できる。
これはmicrosoftのsilverlightのためのRIAであるWPFから出てきた考え方だ。しかし、現在ではknockout.jsやandroid-bindingでも同様のことができる。
(はてぶより追記:WPF上でのMVVMについて、より詳細にはこちらのスライド http://www.slideboom.com/presentations/381148 をご確認ください。)
この考え方をより明快にWebアプリケーションと結びつけようとするむきがある。これがModel driven Viewというgoogleがリードして提案しているDOM拡張である。これは、viewModelにあたるオブジェクトをviewであるdomオブジェクトとmodelオブジェクトがどちらも参照をもっており、それぞれの変更をそれぞれが通知され処理するというものだ。これは、WebComponentsの流れに関連しているものだ。まだ開発中であるがpolymer.jsを利用することである程度先取りすることができる。また、実用を考えるのであれば、angular.jsなどでも近しいの考え方で実装されている。
DCIとMVVMあるいはmdvは今後、アプリケーション開発においては主流になっていくだろう。これによって、Controllerの責務はほぼなくなっていき、Fat Controllerの問題は別の問題にすり替わっていくだろうということが想像される。