cover

概念概覽

flowchart TD
    User["使用者"] -->|發送請求| Controller["Controller<br/>處理請求與協調"]
    Controller -->|操作資料| Model["Model<br/>資料與商業邏輯"]
    Model -->|回傳資料| View["View<br/>使用者介面呈現"]
    View -->|回應畫面| User

    style User fill:#f96,stroke:#333,stroke-width:2px
    style Controller fill:#bbf,stroke:#333
    style Model fill:#bfb,stroke:#333
    style View fill:#fbb,stroke:#333

什麼是 MVC?

想像你走進一間餐廳:菜單(View) 是你看到的東西,廚房(Model) 負責處理食材和料理邏輯,而服務生(Controller) 接收你的點單、把需求傳給廚房、再把做好的菜端到你面前。你不會自己跑進廚房炒菜,廚房也不需要知道菜單長什麼樣——每個角色各司其職。MVC 的精神就是這樣:把「資料」「畫面」「流程控制」拆開來,讓程式碼更好維護。

MVC 是 Model-View-Controller 的縮寫,是一種軟體架構模式。它將應用程式分為三個核心元件,各自負責不同的職責:

元件職責範例
Model資料與商業邏輯資料庫操作、驗證規則
View使用者介面呈現HTML 模板、JSON 回應
Controller處理請求與協調路由處理、呼叫 Model
┌─────────────────────────────────────────────────────────┐
│                                                          │
│  使用者 ──請求──> Controller ──操作──> Model             │
│     ↑                 │                   │              │
│     │                 │                   │              │
│     └───回應───── View <──取得資料────────┘              │
│                                                          │
└─────────────────────────────────────────────────────────┘

MVC 的優點

  1. 關注點分離(Separation of Concerns)

    • 每個元件專注於自己的職責
    • 修改 UI 不影響商業邏輯
  2. 提高可測試性

    • Model 可以獨立進行單元測試
    • 不需要啟動整個應用程式
  3. 便於團隊協作

    • 前端工程師專注 View
    • 後端工程師專注 Model 和 Controller
  4. 程式碼重用

    • 同一個 Model 可以被不同的 View 使用
    • API 和網頁可以共用邏輯

MVC 實作範例

Express.js 實作

Model(models/user.js)

import db from '../database.js';
 
export const User = {
  async findById(id) {
    return db.query('SELECT * FROM users WHERE id = ?', [id]);
  },
 
  async create(data) {
    const { name, email } = data;
    return db.query(
      'INSERT INTO users (name, email) VALUES (?, ?)',
      [name, email]
    );
  },
 
  async update(id, data) {
    const { name, email } = data;
    return db.query(
      'UPDATE users SET name = ?, email = ? WHERE id = ?',
      [name, email, id]
    );
  },
};

Controller(controllers/userController.js)

import { User } from '../models/user.js';
 
export const userController = {
  async show(req, res) {
    const user = await User.findById(req.params.id);
 
    if (!user) {
      return res.status(404).render('error', { message: '使用者不存在' });
    }
 
    res.render('user/show', { user });
  },
 
  async create(req, res) {
    const { name, email } = req.body;
 
    // 驗證
    if (!name || !email) {
      return res.status(400).render('user/new', {
        error: '請填寫所有欄位',
      });
    }
 
    const user = await User.create({ name, email });
    res.redirect(`/users/${user.id}`);
  },
};

View(views/user/show.ejs)

<!DOCTYPE html>
<html>
<head>
  <title><%= user.name %> - 個人資料</title>
</head>
<body>
  <h1><%= user.name %></h1>
  <p>Email: <%= user.email %></p>
  <a href="/users/<%= user.id %>/edit">編輯</a>
</body>
</html>

Routes(routes/users.js)

import express from 'express';
import { userController } from '../controllers/userController.js';
 
const router = express.Router();
 
router.get('/:id', userController.show);
router.post('/', userController.create);
 
export default router;

OK,理論講完了,程式碼也看過了。接下來聊聊一些你在實務上會碰到的爭議——MVC 不是銀彈,它也有被誤用和被挑戰的地方。

MVC 的誤解與爭議

「MVC 是一個巨大誤會」

有人認為現今 Web 開發中的 MVC 與最初 Smalltalk 的 MVC 已經不同:

  1. 原始 MVC(1970s Smalltalk)

    • View 直接監聽 Model 的變化(Observer Pattern)
    • Controller 處理使用者輸入
  2. Web MVC

    • View 不直接監聽 Model
    • Controller 同時處理輸入和協調回應

這種演變是因為 Web 的 Request-Response 模式與桌面應用的持續互動模式不同。

Controller 太肥?

常見問題是 Controller 承擔太多責任,變成「Fat Controller」。解決方案:

  1. Service Layer:將商業邏輯抽到 Service
  2. Repository Pattern:將資料存取邏輯抽出 Model
  3. Form Objects:處理表單驗證
// 使用 Service 避免 Fat Controller
class UserController {
  async create(req, res) {
    const result = await userService.register(req.body);
 
    if (result.error) {
      return res.status(400).json({ error: result.error });
    }
 
    res.status(201).json(result.user);
  }
}

MVC 的變體

模式說明常見框架
MVC經典模式Rails, Laravel, Spring MVC
MVPPresenter 取代 ControllerAndroid(早期)
MVVMViewModel 與 View 雙向綁定Vue.js, Angular
Flux/Redux單向資料流React + Redux

現代前端與 MVC

現代前端框架(React、Vue、Svelte)已經不完全遵循傳統 MVC:

  • Component-Based:View 和部分邏輯合在元件中
  • State Management:使用 Redux、Pinia 管理狀態
  • API-First:前後端分離,後端只提供 API

但 MVC 的核心精神——關注點分離——依然是重要的設計原則。


延伸閱讀

MVC是一個巨大誤會 跟著小明一起搞懂技術名詞:MVC、SPA 與 SSR 架構 後端 MVP 設計原則 前端 MVP 設計原則