MVC・MVVM・MVP の違いを、データの流れで理解する
この 3 つは、よく「似ているけど何が違うのか」と並べて語られます。ですが目的は同じで、 UI 表示のロジックと、ドメイン(データとビジネスルール)を分けることです。 違うのは View と Model をどう仲介し、誰が誰に依存するかだけ。 まずはここを結論として押さえてください。
下のデモで、まったく同じ操作(名前を入力して「挨拶する」を押す)が、 3 つのパターンでどの登場人物をどの順に通って画面更新に至るかを、横並びで見比べられます。 線の向き(誰が誰を知っているか)に注目してください。
① 同じ操作を 3 パターンで流してみる
「次へ」で 1 ステップずつ進めて、操作が登場人物を渡り歩く様子を追えます。 MVC は Model の通知で View が再描画、MVP は Presenter が命令的に View を更新、 MVVM は バインディングで自動同期 —— この差が画面に出ます。
Model-View-Controller
Model の変更通知を View が受けて再描画する(古典 MVC)
- View → Controller入力を渡す
- Controller → ModelModel を更新
- Model ⇢ View変更を通知
- View ⤴ ModelModel を観察して読む
1. ユーザーが名前を入力し、「挨拶する」を押す
Model-View-Presenter
Presenter が View を命令的に更新する(View は受動的)
- View → Presenter操作を通知
- Presenter → ModelModel を操作
- Model ⇢ Presenter結果を返す
- Presenter → View表示を命令
1. ユーザーが名前を入力し、「挨拶する」を押す
Model-View-ViewModel
データバインディングで View と ViewModel が自動同期する
- View ⇄ ViewModelバインディングで同期
- ViewModel ⇄ Viewバインディングで同期
- ViewModel → ModelModel を操作
- Model ⇢ ViewModel結果を返す
1. ユーザーが名前を入力する
なぜ View と Model を分けるのか(共通の動機)
3 つのパターンは、どれも同じ困りごとへの答えです。表示の都合とドメインのルールが 1 か所に 混ざると、画面のコードが膨らみ、テストしづらく、ちょっとした変更があちこちに波及します。 分けておけば、表示を直してもドメインは触らずに済み、ドメインのルールは画面なしで検証できます。
ここで Model はどのパターンでもほぼ同じ役割です。アプリのデータとビジネス ルールを持ち、UI のことを知りません。上のデモで「挨拶文を組み立てる」ロジック (buildGreeting)が Model 側に置かれ、3 パターンで共通して使えるのはそのためです。 違うのは Model の前に立つ仲介役の流儀だけ、という見方ができます。
MVC(Model-View-Controller)
起源は Smalltalk-80 で、Trygve Reenskaug が考案したとされます。古典的な MVC の役割分担は こうです。
- Controller:ユーザー入力(クリック等)を受け取り、Model を更新する。入力の受け口。
- Model:データとビジネスルール。状態が変わると Observer(通知)で View に知らせる。
- View:Model を観察して自分を再描画する。View が Model を直接読むことがある。
つまり古典 MVC では、View と Model のあいだに直接の線がありうるのが特徴です (View → Model の参照、Model → View の通知)。Controller はあくまで入力を受けて Model を 動かす係で、画面の更新そのものは Model の通知をきっかけに View 自身が行います。
Rails・Laravel・Spring MVC などサーバーサイドの「MVC」は、古典 MVC と少しずれます。 そこでは「Controller がリクエストを受ける → Model を操作 → View(テンプレート)にデータを 渡して HTML を描画」という流れで、Model から View への Observer 通知はありません。 同じ名前でも前提が違うので、どちらの MVC を指しているかを意識すると混乱が減ります。
弱点もあります。Controller や View に責務が集まると 「Fat Controller」「Massive View Controller」(iOS 開発で有名な呼び名)になりやすく、 Model と View が直接結合するとテストしづらくなります。
MVP(Model-View-Presenter)
MVP の View は受動的(Passive View)です。View は「ボタンが押された」を Presenter に通知し、表示の指示(「このラベルにこの文字を出せ」)を Presenter から受けるだけで、 自分ではロジックを持ちません。
- Presenter:View と Model の間を明示的に仲介する。Model を操作し、結果を View インターフェース越しに命令的に反映する(
view.setGreeting("...")のように呼ぶ)。 - View:インターフェースとして抽象化される。Presenter はその抽象に依存する。
View と Presenter は基本 1 対 1です。View が抽象(インターフェース)になっている おかげで、Presenter は View をモックに差し替えて単体テストできます。これが MVP の 大きな利点です。MVC との違いははっきりしていて、MVP では Model と View が直接やりとりしません(やりとりは Presenter 経由)。
MVVM(Model-View-ViewModel)
MVVM の ViewModel は、View に見せるための状態(プロパティ)とコマンドを公開します。 View の状態を保持しますが、View への参照は持ちません(View を知らない)。
View と ViewModel はデータバインディングで結ばれ、自動で同期します (双方向 or 単方向)。View でテキストが変われば ViewModel のプロパティが更新され、ViewModel が 変われば View が再描画されます。MVP のように view.setXxx() を命令的に呼ぶ必要が ありません。ここが MVP との決定的な違いです。MVP の Presenter は View を知っていて 命令的に更新しますが、MVVM の ViewModel は View を知らず、バインディングが宣言的につなぎます。
起源と代表例は、Microsoft の WPF / Silverlight(John Gossman が提唱)、Knockout.js など。 広い意味では Vue や Angular のリアクティブシステムも同じ系譜に置けます。
3 つの決定的な違い
違いは「View と Model をどう仲介するか」に集約されます。表で並べると、仲介役の種類と View 更新の仕組みが軸になっているのが分かります。
| 観点 | MVC | MVP | MVVM |
|---|---|---|---|
| 仲介役 | Controller | Presenter | ViewModel |
| View ↔ Model の直接線 | ありうる(View が Model を観察) | なし(Presenter が間に立つ) | なし(ViewModel 経由) |
| View 更新の仕組み | Model の変更通知で再描画 | Presenter が命令的に呼ぶ | データバインディングで自動 |
| View ↔ 仲介役の関係 | Controller は複数 View も扱える | 基本 1 対 1 | ViewModel は View を知らない |
| テスト容易性の勘所 | Model は可、View 結合が難 | View モックで Presenter を単体テスト | ViewModel を単体テスト |
View 更新の仕組みに注目すると流れがつかめます。MVC は Model の変更通知で View が自分を描き直し、MVP は Presenter が view.setXxx() で命令的に押し込み、 MVVM はバインディングが自動でつなぐ。命令的(MVP)から宣言的(MVVM)への移行が、この 3 つの 並びの分かりやすい流れです。
「Vue はどれ? React は?」
Vue はリアクティブなデータバインディングを持つので MVVM 寄りです。 ref / reactive が ViewModel 的、template が View、 composable や store が Model 的、という対応で見られます。テンプレートに値を書けば自動で 画面が同期するのは、まさに MVVM のバインディングの考え方です。
React は単方向データフロー+明示的な更新(setState / フック)なので、3 つのどれにもきれいには収まりません。あえて当てはめれば、コンポーネントが View、state やフックが仲介役、という見方になりますが、無理に分類するより 「なぜ分離するのか」を見たほうが実りがあります。
どのフレームワークがどの分類か、を厳密に決めることにあまり意味はありません。 3 つに共通する狙いは 変更の波及を抑え、テストしやすくすること。 そのために View と Model を分け、仲介役を単体テストできる形にしておく —— ここが押さえどころです。
よくある誤解・バッドケース
- テンプレートにビジネスロジックを直書き:View に
if分岐だらけの判定や、DB アクセスを書いてしまう。表示とドメインが混ざって、どちらのテストも難しくなる。 - 仲介役が全部抱える:Controller / Presenter / ViewModel に計算もデータ取得も詰め込み、Model が空洞になる。Fat Controller / Massive ViewModel はこの形。
- 名前だけで区別しようとする:仲介役の呼び名(Controller / Presenter / ViewModel)よりも、View 更新が通知なのか命令なのかバインディングなのか、View と Model に直接線があるかを見たほうが違いが分かる。
グッドケースは逆向きに考えれば出てきます。ドメインを Model に閉じ込め、UI 都合の状態だけを 仲介役に置き、View は表示に徹する。そして仲介役を単体テストできる状態にしておく —— 3 パターンに共通する良い形です。
まとめ
MVC・MVP・MVVM は、View と Model の仲介の仕方が違うだけで、Model の役割と 「分離して変更波及を抑える」という狙いは共通です。MVC は Model の通知で View が再描画、 MVP は Presenter が命令的に View を更新、MVVM はバインディングで自動同期。 View ↔ Model の直接線は古典 MVC にだけありえて、MVP / MVVM では仲介役が間に立ちます。
どの名前を選ぶかより、誰が誰を知っているか(依存方向)と、View 更新の仕組みを 把握しておくほうが実務では役に立ちます。デモの 3 パネルで線の向きを見比べた感覚を、 そのまま持ち帰ってもらえれば十分です。
出典・参考
- Martin Fowler “GUI Architectures”(MVC / MVP / Passive View / Supervising Controller の整理)
- Trygve Reenskaug — MVC(Smalltalk-80 起源)
- Microsoft の MVVM 解説(John Gossman 由来)
- iOS 開発における “Massive View Controller” の議論
本ページは上記の議論を、操作しながら学べる教材として再構成したものです。