エラノート エラノート

MVCモデルとは?Webアプリの設計パターンを解説

MVC 設計パターン Webアプリ Django 入門
広告スペース (article-top)

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の処理の流れを見てみましょう。

タスクの一覧を表示する場合

  1. ユーザーがブラウザで /tasks にアクセスする
  2. Controllerが「タスク一覧の表示」リクエストを受け取る
  3. ControllerがModelに「タスクの一覧を取得して」と依頼する
  4. Modelがデータベースからタスクのデータを取得して返す
  5. Controllerが取得したデータをViewに渡す
  6. ViewがデータをもとにHTMLを生成する
  7. ブラウザにHTMLが返され、画面が表示される

新しいタスクを作成する場合

  1. ユーザーがフォームにタスク名を入力して送信する
  2. Controllerが「タスクの作成」リクエストを受け取る
  3. ControllerがModelに「このデータで新しいタスクを作って」と依頼する
  4. Modelがデータを検証し、問題なければデータベースに保存する
  5. 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つの役割を意識してコードを分けてみてください。

広告スペース (article-bottom)

あわせて読みたい