Model View ViewModel
Model-View-ViewModel (MVVM、モデル・ビュー・ビューモデル) はUIを持つソフトウェアに適用されるソフトウェアアーキテクチャの一種である。
MVVMはソフトウェアをModel・View・ViewModelの3要素に分割する。プレゼンテーションとドメインを分離し(V-VM / M)また宣言的Viewを分離し状態とマッピングを別にもつ(V / VM)ことでソフトウェアの保守性・開発生産性を向上させる。
Model-View-ViewModelパターンはModel-View-Controller (MVC) パターンの派生であり、特にPresentation Model パターンを直接の祖先に持つ。元来マイクロソフトのユーザインタフェースサブシステムであるWindows Presentation Foundation (WPF) やSilverlightの世界で生まれた考え方ではあるが、現在はAndroidやウェブブラウザ上でのJavaScriptの世界でもMVVMの利用は広がっている。
Microsoft MVPのJosh Smithのリポートより
2005年に、マイクロソフトのWPFおよびSilverlightアーキテクトであったJohn Gossmanが自身のブログでModel-View-ViewModel (MVVM) パターンを発表しました。MVVMとFowlerのプレゼンテーションモデルは、両方ともビューの状態と動作を含んだ形でビューを抽象化しています。Gossmanはユーザインタフェースの作成を簡素化するために、WPFの活用コア機能への標準化された方法としてMVVMを導入し、一方、FowlerはビューのUIプラットフォームに依存しない抽象化を作成するための手段としてプレゼンテーションモデルを導入しました。その意味で、一般的なPMパターンをWPFとSilverlightのプラットフォームにより特化したものになるようにMVVMを検討しています。
MVVMパターンは、2006年11月21日にリリースされた.NET Framework 3.0に実装されたWPFとSilverlightの両方をサポートするために考案された。しかし、MVVMパターンは今日では
WPFに取り組んでいる何人かのマイクロソフトアーキテクト (クリエーターのJohn Gossman、Microsoft MVPのJosh Simith、MicrosoftプログラムマネージャーのKarl Shifflett) はMVVMについてオンライン上で幅広く情報発信している。
さらに今日では、パターンはmodel-view-binder (MVB) としても記載されている。
UIを持つソフトウェアはしばしばドメインを司るエンジニアとUIを司るデザイナーの協業によって開発されるが、彼らが用いるツール・言語は必ずしも一致しない。MVVMが解決する課題の1つ目はこの不一致の許容である。
ドメインデータとUIすなわちModelとViewは、表示(model→view)と操作(view→model)の関係をもつ。シンプルなケースではmodelの状態がそのままviewに表示され、viewの操作がそのままmodelの状態変更に適用できる(データバインディング)。しかし現実にはviewとmodelは1対1対応しない。model内の複数の要素から算出される値がviewでの表示に相応しい場合や、viewの操作に基づいた演算結果をmodelに適用したい場合(viewではなく演算だがドメインロジックではないケース)が頻出する。結果として状態を持つmodelとは別に、view自体にも状態を持たせる必要が出てくる。MVVMが解決するもう1つの課題はこのviewが持つ状態の表現である。
model-viewでの開発プロセス差異(課題1)を許容しかつview状態を表現する(課題2)ために、MVVMパターンではソフトウェアをModel (モデル)、View (ビュー)、ViewModel (ビューモデル) に三分割する。
MVVMではmodelとviewを明示的に分離する(関心の分離#プレゼンテーションとドメインの分離)。これによりmodelとviewを異なるツールで異なる人が開発でき、効率よい協業が可能になる。
またMVVMでは「Viewのためのモデル(model for a view)」としてViewModelを導入する。ViewModelはViewとModelの中間に存在し、表示のためにmodel値をデータバインディング可能な形式へ変換して状態として保持する。また操作のために演算用コマンド(ハンドラ)を有している。このViewModelによる状態表現により、modelをviewに関知せず構築し、viewはデータバインディングを介したUI表示のみに特化させながら、model-view間をシームレスに繋ぐことが可能になる。
MVVMでは、プログラムを3つの要素、Model (モデル)、View (ビュー)、ViewModel (ビューモデル) に分割する。
アプリケーションのドメイン(問題領域)を担う、そのアプリケーションが扱う領域のデータと手続き(ビジネスロジック - ショッピングの合計額や送料を計算するなど)を表現する要素である。
多くのアプリケーションではデータの格納に永続的な記憶の仕組み(データベースなど)が使われていたり、サーバが別途存在するアプリケーションではサーバ側との通信ロジックなどが含まれている。MVVMの概念ではMVCの概念と同様に、データの(UI以外の)入出力は取り扱わないので、強いて言うならばそれらはModelの中に隠蔽されると考えられる。
一般的にModelはドメインを担当すると言われるがこの言葉だけをもってModelの役割を想像するのは難しい。たとえばクライアントサーバモデルのアプリケーションのクライアントアプリケーション側は、そのドメインそのものがプレゼンテーションになっている。アプリケーションをプレゼンテーションとドメインに分けて考えようとした際にはこの事が混乱の一因となっている。Modelの役割は、後述するViewとViewModelの役割以外の部分と考えるのが妥当である。
ModelはViewの描画について知らないし、知る必要もない。ここで重要なのは、Modelは描画について関わらないだけで、Viewの見た目、装飾についての情報を保持したりするのはMVVMパターンとして何の問題も無いということである。つまり、そのアプリケーションが背景色や文字色、コントロール間の余白といった表示をカスタマイズできる場合、その背景色や文字色や余白のサイズを保持するのはModelである。ただし上述した通りModelは描画には関わることは無く、その情報を元に実際に描画するのはViewの役目である。(もちろんこのModelの情報はViewModelを経由してバインドされる。)
View(ビュー)はアプリケーションの扱うデータをユーザーが見るのに適した形で表示し、ユーザーからの入力を受け取る要素である。すなわちユーザインタフェースの入出力が責務である。Viewは宣言的に定義され、渡された値に基づいて描画をおこない、ユーザー入力を通知する。よってMVVMにおけるViewは状態を持たない。
実装ではしばしば、ViewModelとのデータバインディングを介した値の取得と描画・ユーザー入力の通知がおこなわれる。そのためテンプレートエンジンや宣言型のドメイン固有言語 (DSL) にViewを委譲する形のプラットフォームと非常に相性がよくなる。
ViewModel(ビューモデル)はViewを描画するための状態の保持と、Viewから受け取った入力を適切な形に変換してModelに伝達する役目を持つ。すなわちViewとModelの間の情報の伝達と、Viewのための状態保持のみを役割とする要素である。
実装ではしばしば、Viewとの通信はデータバインディング機構のような仕組みを通じて行う。その場合ViewModelの変更は開発者から見て自動的にViewに反映される。
MVVMはModel-View-Controller(MVC)から派生したアーキテクチャであり、いくつかの違いがある。
まずMVVMのViewはMVCにおけるControllerを包含している。これは現代のGUIプラットフォームがControllerがかつて担っていた仕事の殆どを抽象化しており、thin Controllerをわざわざ区別する必要性が低いからである。当初(MVCが提唱されたのは1979年)のMVCはマウスカーソルがどのView要素上に位置するかController側でハンドリングしていたが、現代のWebブラウザはそれを全て自動で行っている。
またMVVMのViewは宣言的に定義され状態を持たない。MVCにおけるViewは状態を持つViewクラスであり、Modelには含まれないView用の状態(View State。例: UI遷移中を示す属性)を保持していた。MVVMにおいてこの責務はViewModelへと移され、ViewはUIの定義と値のマッピングのみに責務を持っている。
すなわちMVVMは、MVCをModel-Viewと解釈した上でViewを宣言型とし、Modelへの参照とView状態をもつ仲介役としてViewModelを導入している。
MVVMと同様にMVCの派生パターンであり、アプリケーションをView - Presentation Model - Modelに分割する形のPresentation Modelパターンというパターンがある。
Presentation ModelパターンのPresentation ModelはMVVMのViewModelと同様にViewの状態を保持し、ViewはPresentation Modelの状態をデータバインディング機構のような仕組みを通じて自動的に描画するのみである。
一見MVVMとの区別はつかないが、もともとMVVMという言葉が提唱されたWindows Presentation Foundation (WPF)の世界でのMVVMにはPresentation Modelより一歩進んだ特徴がある。WPFでViewを担当するXAMLというXMLベースのDSLは非常に高機能で、Viewを完全にXAMLだけで実装する事やViewModelに対してViewを完全に抽象化する(異なったViewで差し替え可能にする)ことも可能となっている。
MVVMはソフトウェアアーキテクチャパターンであり、フレームワーク・ライブラリではない。よってMVVMを採用する際はそれに合わせたモジュール分割をおこない、場合によってはMVVMを支援するフレームワーク・ライブラリを採用する。
一般には1つのModelを用意し、View全体をViewコンポーネント群へと分割、橋渡しとして各Viewに対してViewModelを設定する(1:N:N)。
Windows Presentation Foundation (WPF)
Windowsランタイム (WinRT)
ユニバーサルWindowsプラットフォーム(UWP)
Xamarin.Forms
ViewModelの責務はModel-View間の形を合わせるための変換と値の保持であり、そのためにViewModelはModel変換の演算をおこなう。しかしこの演算においてModelが担うべき演算まで実装してしまう危険性があり、しばしば起きるこの実装ミスとそれによる肥大化はファットViewModelと呼ばれる。
Viewでも同様の問題が起きうる。Viewはコマンド発行演算のみをおこなうべきだが、コマンドの引数を計算する過程などでビジネスロジックを埋め込む実装ミスがしばしば発生する。
1つのモデルと1つのViewを仲介するためにViewModelは存在する。しかし実装上このViewModelを他のViewModelから利用してしまう場合がある。例えばVM1が持っている値が他のViewModel (VM2) でも利用できる形に整形されていたとする。VM2は変換を実装せずともVM1を参照・監視するだけで適切な値を得ることができてしまう。もしVM1がVM2の値を同様に参照すると容易に循環参照が発生する。VM1はVM2の値変更を監視しているため、VM2の変更でVM1が変更されそれによりVM2が変更され…とループが走り、システムの状態が予測不可能になる(デバッグが著しく困難になる)。