MVCモデルとは?Webアプリの設計パターンを解説
MVCは「Model-View-Controller」の略で、アプリケーションの構造を3つの役割に分ける設計パターンです。コードの見通しを良くし、修正や機能追加をしやすくする効果があります。この記事では、MVCの基本的な考え方とWebアプリケーションでの実装例を解説します。
MVCモデルとは何か
MVCモデルは、アプリケーションのコードを「Model(モデル)」「View(ビュー)」「Controller(コントローラー)」の3つの役割に分離する設計パターンです。1つのファイルにすべてを詰め込むのではなく、役割ごとにコードを分けることで、変更の影響範囲を限定できます。
身近な例で理解する
レストランの運営にたとえると、MVCの3つの役割がわかりやすくなります。
- Model(厨房): 食材の管理と調理を担当する。お客さんとは直接関わらない
- View(客席・メニュー表): お客さんに料理を見せる。調理はしない
- Controller(ホールスタッフ): お客さんの注文を受けて厨房に伝え、できた料理をお客さんに届ける
なぜ役割を分離するのか
たとえばメニュー表のデザインを変えたいとき、厨房の調理方法を変える必要はありません。逆に、レシピを改良しても、ホールスタッフの業務手順は変わりません。
プログラムも同じです。画面のデザインを変更するとき、データベースの処理を修正する必要がなければ、バグを生むリスクが減ります。役割の分離は、変更に強いコードを作るための基本的な方針です。
Modelの役割
Modelはアプリケーションの「データ」と「ビジネスロジック」を担当します。データベースとのやり取りや、データの加工・検証がModelの仕事です。
Modelが担当すること
- データベースへの読み書き
- データの検証(バリデーション)
- ビジネスルールの実装
Pythonでの例
class Task:
def __init__(self, title, done=False):
self.title = title
self.done = done
def mark_as_done(self):
self.done = True
def validate(self):
if not self.title:
raise ValueError("タイトルは必須です")
if len(self.title) > 100:
raise ValueError("タイトルは100文字以内にしてください")
Modelで注意すべき点
Modelには「どう表示するか」の情報を含めないことが重要です。HTMLのタグやCSSのクラス名がModelに入り込んでいたら、設計を見直す必要があります。Modelはデータの管理に徹するべきです。
Viewの役割
Viewはユーザーに情報を「見せる」部分を担当します。Webアプリケーションでは、HTMLの生成やテンプレートの描画がViewの仕事です。
Viewが担当すること
- データの表示(HTML、JSONなど)
- ユーザーインターフェースの構築
- テンプレートの描画
HTMLテンプレートの例
Webフレームワークでは、テンプレートエンジンを使ってViewを作成することが一般的です。
<!-- タスク一覧の表示テンプレート -->
<h1>タスク一覧</h1>
<ul>
{% for task in tasks %}
<li>
{{ task.title }}
{% if task.done %}
<span>-- 完了</span>
{% endif %}
</li>
{% endfor %}
</ul>
Viewで注意すべき点
Viewにビジネスロジックを書かないことが大切です。テンプレートの中で複雑な計算やデータベースへの問い合わせを行うと、コードが散らばって保守しにくくなります。Viewの仕事は「受け取ったデータを見やすく表示する」ことだけです。
Controllerの役割
Controllerは、ModelとViewの橋渡し役です。ユーザーからのリクエストを受け取り、Modelにデータの処理を依頼し、その結果をViewに渡します。
Controllerが担当すること
- ユーザーリクエストの受け取りと解析
- Modelの呼び出し
- 適切なViewの選択とデータの受け渡し
Pythonでの例
class TaskController:
def __init__(self):
self.tasks = [] # 本来はデータベースを使う
def index(self, request):
"""タスク一覧を表示"""
return render("task_list.html", {"tasks": self.tasks})
def create(self, request):
"""新しいタスクを作成"""
title = request.get("title")
task = Task(title)
try:
task.validate()
self.tasks.append(task)
return redirect("/tasks")
except ValueError as e:
return render("task_form.html", {"error": str(e)})
def complete(self, request, task_id):
"""タスクを完了にする"""
task = self.tasks[task_id]
task.mark_as_done()
return redirect("/tasks")
Controllerで注意すべき点
Controllerが肥大化する(ファットコントローラ)のはよくある問題です。Controllerにビジネスロジックを書きすぎると、Modelの役割まで担ってしまいます。Controllerは「交通整理」に徹し、複雑な処理はModelに任せるのが原則です。
MVCの処理の流れ
Webアプリケーションにおける典型的なMVCの処理の流れを見てみましょう。
タスクの一覧を表示する場合
- ユーザーがブラウザで
/tasksにアクセスする - Controllerが「タスク一覧の表示」リクエストを受け取る
- ControllerがModelに「タスクの一覧を取得して」と依頼する
- Modelがデータベースからタスクのデータを取得して返す
- Controllerが取得したデータをViewに渡す
- ViewがデータをもとにHTMLを生成する
- ブラウザにHTMLが返され、画面が表示される
新しいタスクを作成する場合
- ユーザーがフォームにタスク名を入力して送信する
- Controllerが「タスクの作成」リクエストを受け取る
- ControllerがModelに「このデータで新しいタスクを作って」と依頼する
- Modelがデータを検証し、問題なければデータベースに保存する
- Controllerがタスク一覧ページにリダイレクトする
実際のフレームワークでのMVC
主要なWebフレームワークはMVCまたはそれに類する構造を採用しています。
DjangoのMTV
DjangoはMVCを「MTV(Model-Template-View)」という名前で採用しています。名前は違いますが、概念は同じです。
- Model → Model(データとビジネスロジック)
- Template → View(表示テンプレート)
- View → Controller(リクエスト処理)
# models.py(Model)
from django.db import models
class Task(models.Model):
title = models.CharField(max_length=100)
done = models.BooleanField(default=False)
# views.py(Djangoでは「View」がControllerの役割を担う)
from django.shortcuts import render, redirect
from .models import Task
def task_list(request):
tasks = Task.objects.all()
return render(request, "tasks/list.html", {"tasks": tasks})
def task_create(request):
if request.method == "POST":
title = request.POST.get("title")
Task.objects.create(title=title)
return redirect("task_list")
return render(request, "tasks/form.html")
<!-- templates/tasks/list.html(Template = MVCのView) -->
<h1>タスク一覧</h1>
<ul>
{% for task in tasks %}
<li>{{ task.title }}</li>
{% endfor %}
</ul>
Djangoの「View」はMVCの「Controller」に相当する点に注意してください。名前の違いに惑わされず、役割で理解することが大切です。
Express(Node.js)でのMVC
// models/task.js(Model)
class Task {
constructor(title) {
this.title = title;
this.done = false;
}
}
// controllers/taskController.js(Controller)
const tasks = [];
function listTasks(req, res) {
res.render("tasks/list", { tasks });
}
function createTask(req, res) {
const task = new Task(req.body.title);
tasks.push(task);
res.redirect("/tasks");
}
MVCの派生パターン
MVCから派生した設計パターンもいくつか存在します。
MVVM(Model-View-ViewModel)
Vue.jsやReactなどのフロントエンドフレームワークで使われる考え方です。ViewとModelの間に「ViewModel」を置き、データの変更が自動的にViewに反映されます(データバインディング)。
MVPとMVCの違い
MVP(Model-View-Presenter)は、ViewとModelが直接やり取りしない点がMVCとの違いです。すべてのやり取りがPresenterを経由します。
これらの派生パターンは、用途や技術に応じてMVCを発展させたものです。基本的な「役割を分離する」という考え方は共通しています。
まとめ
この記事では、MVCモデルの基本を解説しました。
- MVCはModel・View・Controllerの3つに役割を分離する設計パターン
- Modelはデータとビジネスロジック、Viewは表示、Controllerは橋渡しを担当
- 役割を分離することで、変更の影響範囲が限定され保守しやすくなる
- DjangoではMTV、フロントエンドではMVVMなど派生パターンがある
- Controllerにロジックを詰め込みすぎない(ファットコントローラを避ける)
MVCの考え方は、特定のフレームワークに限らず、コードを整理するための基本的な指針です。まずは小さなWebアプリケーションを作りながら、3つの役割を意識してコードを分けてみてください。