MVC: bản chất của mẫu Model-View-Controller và cách triển khai

Tác giả: IT Sectr Đã đăng: 2026-02-16 Thời gian đọc: 9 phút

MVC (Model-View-Controller) là một mẫu kiến trúc chia ứng dụng thành ba thành phần: Model chịu trách nhiệm về dữ liệu và logic nghiệp vụ, View chịu trách nhiệm về giao diện người dùng, Controller chịu trách nhiệm xử lý đầu vào và điều phối Model và View. Trên iOS, MVC được triển khai qua UIViewController, trên Android — qua Activity và Fragment. MVC vẫn là mẫu cơ bản mà MVVM, MVP và Clean Architecture được xây dựng dựa trên. Tìm hiểu thêm tại MVC in Cocoa Core.

Những điểm chính

  • MVC — ba thành phần: Model (dữ liệu), View (giao diện), Controller (logic)
  • UIViewController — triển khai Controller trên iOS, chịu trách nhiệm vòng đời màn hình
  • Activity/Fragment — triển khai Controller trên Android với chức năng tương tự
  • Massive View Controller — vấn đề chính của MVC: bộ điều khiển phình to đến hàng nghìn dòng
  • Giao tiếp thành phần — Controller cập nhật View và Model, Model thông báo Controller về thay đổi

MVC là gì: bản chất của mẫu Model-View-Controller

MVC (Model-View-Controller) là một mẫu kiến trúc được Trygve Reenskaug đề xuất vào năm 1979 cho ngôn ngữ Smalltalk-80. Mẫu này chia ứng dụng thành ba lớp: Model chứa dữ liệu và logic nghiệp vụ, View chịu trách nhiệm hiển thị, Controller xử lý đầu vào của người dùng và cập nhật Model và View. Sự phân tách trách nhiệm cho phép thay đổi từng lớp một cách độc lập — ví dụ: thay thế View từ UIKit sang SwiftUI mà không thay đổi logic nghiệp vụ trong Model.

Tương tác thành phần trong MVC theo một chu trình: người dùng tương tác với View → Controller nhận sự kiện → Controller cập nhật Model → Model thông báo Controller về thay đổi → Controller cập nhật View. Trong triển khai cổ điển, Model sử dụng mẫu Observer: khi dữ liệu thay đổi, Model gửi thông báo, Controller đăng ký và cập nhật View. Trong triển khai của Apple, Key-Value Observing (KVO) hoặc NotificationCenter thực hiện vai trò này.

Thành phầnTrách nhiệmVí dụ trên iOSVí dụ trên Android
ModelDữ liệu, logic nghiệp vụ, mạngStruct User, CoreDataData class, Repository
ViewHiển thị UIStoryboard, XIB, UIViewXML layout, Jetpack Compose
ControllerXử lý đầu vào, điều phốiUIViewControllerActivity, Fragment

MVC trong phát triển di động hiện đại được sử dụng ít hơn so với 10 năm trước, nhưng vẫn cần thiết để hiểu. Apple khuyến nghị MVC cho các màn hình đơn giản trong ứng dụng UIKit. Google không khuyến nghị MVC thuần túy cho Android — tài liệu chính thức đề xuất MVVM với Jetpack. Tuy nhiên, kiến thức về MVC là cần thiết để làm việc với các dự án kế thừa và để hiểu sự tiến hóa của các mẫu kiến trúc.

MVC trên iOS: UIViewController và Storyboard

Apple MVC là một triển khai tùy chỉnh của mẫu được tích hợp trong UIKit. UIViewController hoạt động như Controller: quản lý vòng đời màn hình (viewDidLoad, viewWillAppear, viewDidDisappear), xử lý chạm và hành động của người dùng, cập nhật View qua IBOutlets. View được tạo trong Interface Builder (storyboard hoặc XIB) hoặc theo chương trình. Model — bất kỳ đối tượng dữ liệu nào: dịch vụ mạng, ngăn xếp CoreData, cấu trúc Swift.

swift
final class UserViewController: UIViewController {
    // View (qua storyboard outlet)
    @IBOutlet private var nameLabel: UILabel!
    @IBOutlet private var emailLabel: UILabel!

    // Model
    private let userService = UserService()

    override func viewDidLoad() {
        super.viewDidLoad()
        loadUser()
    }

    private func loadUser() {
        userService.fetchUser { [weak self] user in
            // Controller cập nhật View
            self?.nameLabel.text = user.name
            self?.emailLabel.text = user.email
        }
    }
}

Vấn đề của Apple MVC — View và Controller được kết hợp chặt chẽ. UIViewController quản lý đồng thời cả View và logic. Storyboard lưu trữ View trong XML, nhưng bộ điều khiển có tham chiếu trực tiếp đến các phần tử UI qua IBOutlets. Điều này vi phạm nguyên tắc đơn trách nhiệm: bộ điều khiển chịu trách nhiệm về vòng đời, ủy quyền, datasource, target-action và hoạt ảnh. Kết quả là, một màn hình ứng dụng iOS tiêu chuẩn chứa 200–500 dòng trong bộ điều khiển.

Vòng đời ViewController — Apple cung cấp 6 phương thức vòng đời: loadView (tạo View thủ công), viewDidLoad (sau khi tải View vào bộ nhớ), viewWillAppear (trước khi xuất hiện trên màn hình), viewDidAppear (sau hoạt ảnh), viewWillDisappear (trước khi rời màn hình), viewDidDisappear (sau khi rời). Mỗi phương thức là một nơi để đặt logic trong MVC. Sử dụng các phương thức này cho logic nghiệp vụ làm tăng tốc sự phình to của bộ điều khiển.

MVC trên Android: Activity, Fragment và XML Layout

Android MVC — Activity và Fragment hoạt động như Controller, tệp XML layout như View, bất kỳ lớp POJO nào có dữ liệu như Model. Activity quản lý vòng đời màn hình: onCreate, onStart, onResume, onPause, onStop, onDestroy. Fragment là một màn hình con trong Activity với vòng đời riêng. View (XML) được tách khỏi Controller và được tải qua setContentView hoặc LayoutInflater. Model — kho lưu trữ, cơ sở dữ liệu, lệnh gọi mạng.

kotlin
class UserActivity : AppCompatActivity() {
    // View qua XML layout
    private lateinit var binding: ActivityUserBinding

    // Model
    private val userRepository = UserRepository()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityUserBinding.inflate(layoutInflater)
        setContentView(binding.root)
        loadUser()
    }

    private fun loadUser() {
        userRepository.getUser { user ->
            runOnUiThread {
                binding.nameText.text = user.name
                binding.emailText.text = user.email
            }
        }
    }
}

Android ViewBinding và DataBinding — các công cụ hiện đại giảm sự kết hợp giữa Controller và View. ViewBinding tạo một lớp có tham chiếu trực tiếp đến Views từ XML, loại bỏ findViewById. DataBinding thêm khả năng liên kết dữ liệu với UI trong đánh dấu XML qua @{user.name}. DataBinding là một bước tiến tới MVVM, vì nó cho phép truyền dữ liệu từ Model sang View mà không cần mã trong Activity. Google khuyến nghị DataBinding cho tất cả các dự án mới.

Vòng đời Android phức tạp hơn iOS: Activity có thể bị hủy và tạo lại khi xoay màn hình, thiếu bộ nhớ hoặc thay đổi cấu hình. Trong MVC thuần túy, bộ điều khiển (Activity) chứa logic bị mất khi bị hủy. Điều này đòi hỏi phải lưu trạng thái qua onSaveInstanceState hoặc ViewModel từ Jetpack, vượt ra ngoài MVC thuần túy và đưa kiến trúc đến gần MVVM hơn.

Massive View Controller và hạn chế của MVC

Massive View Controller là thuật ngữ mô tả vấn đề chính của MVC trong phát triển di động. Bộ điều khiển trên iOS và Android đảm nhận quá nhiều trách nhiệm: xử lý đầu vào, xác thực dữ liệu, tương tác mạng, điều hướng, lưu cache, hoạt ảnh, quản lý vòng đời. Kết quả là, bộ điều khiển phình to đến 500–2000 dòng mã, trở nên khó đọc, khó kiểm thử và khó bảo trì.

Nguyên nhân của Massive View Controller — kiến trúc của UIKit và Android Framework khuyến khích đặt logic trong bộ điều khiển. Lệnh gọi mạng, xử lý JSON, điều hướng — tất cả những điều này tự nhiên nằm trong Activity hoặc UIViewController vì chúng có quyền truy cập vào vòng đời và UI. Nhà phát triển phải chủ động trích xuất logic vào các lớp riêng biệt (Service, Manager, Interactor), điều này đòi hỏi kỷ luật và hiểu biết về các nguyên tắc kiến trúc.

Vấn đề MVCMô tảGiải pháp
Kết hợp chặt chẽController biết về View và ModelMVVM — ViewModel không biết về View
Phức tạp kiểm thửController phụ thuộc vào UIKit/AndroidTrích xuất logic vào dịch vụ
Vòng đờiTrạng thái bị mất khi xoayViewModel từ Jetpack/SwiftUI
Thiếu điều hướngController quản lý chuyển tiếpMẫu Coordinator, Router

Kiểm thử MVC — Model được kiểm thử riêng lẻ bằng kiểm thử đơn vị. Controller khó kiểm thử do phụ thuộc vào UIKit/UIFoundation. XCTest không cho phép tạo UIViewController mà không có cửa sổ xem. Đối với Android, ActivityTestRule và Robolectric giải quyết một phần vấn đề, nhưng kiểm thử chậm. View thường không được kiểm thử bằng kiểm thử đơn vị — cho UI, kiểm thử ảnh chụp màn hình và kiểm thử UI (XCUITest, Espresso) được sử dụng.

Khi nào MVC hợp lý — màn hình đơn giản với một hoặc hai phần tử (màn hình đăng nhập, hồ sơ, cài đặt). Nguyên mẫu và MVP để xác thực giả thuyết — MVC viết nhanh hơn mà không cần các lớp bổ sung. Dự án với cơ sở mã nhỏ lên đến 10–15 màn hình. Trong các dự án phức tạp, MVC dẫn đến tích lũy nợ kỹ thuật và yêu cầu tái cấu trúc mỗi 6–12 tháng.

So sánh MVC với MVVM, MVP và Clean Architecture

MVC vs MVVM — sự khác biệt chính: trong MVVM, bộ điều khiển được thay thế bằng ViewModel không có tham chiếu đến View. Dữ liệu được truyền qua Observable (SwiftUI), LiveData/StateFlow (Android) hoặc Combine/RxSwift. ViewModel có thể kiểm thử bằng kiểm thử đơn vị mà không có phụ thuộc UI. Apple khuyến nghị MVVM với SwiftUI từ năm 2019, Google — MVVM với LiveData/Flow là kiến trúc Android chính thức. MVVM yêu cầu nhiều mã hơn cho việc liên kết nhưng cải thiện đáng kể khả năng kiểm thử.

MVC vs MVP — trong MVP (Model-View-Presenter), Presenter là một lớp có thể kiểm thử nhận View qua một giao diện. Không giống như MVC nơi Controller quản lý trực tiếp View qua UIKit, Presenter không phụ thuộc vào framework — nó hoạt động thông qua trừu tượng ViewInterface. MVP phổ biến trong phát triển Android trước Jetpack và được sử dụng trong các dự án kế thừa. Presenter tồn tại lâu hơn Activity và giữ trạng thái khi xoay màn hình.

MVC vs Clean Architecture — Clean Architecture thêm các lớp: Use Cases (Interactors), Entities, Gateways và Repository. MVC vẫn nằm trong lớp Presentation, nhưng logic nghiệp vụ được chuyển đến lớp Domain với Use Cases. Clean Architecture giải quyết triệt để vấn đề Massive View Controller — Controller chỉ chứa các lệnh gọi Use Cases và cập nhật View. Nhược điểm là số lượng lớp và tệp tăng đáng kể, điều này hợp lý cho các dự án có 50+ màn hình.

swift
// MVC trên iOS: Controller chứa mọi thứ
class OrderViewController: UIViewController {
    func placeOrder() {
        // Xác thực + mạng + cập nhật UI
        guard Validation.isValid(total) else { return }
        NetworkService.shared.submit(order) { [weak self] result in
            self?.handleResult(result)
        }
    }
}

// MVVM: logic trong ViewModel
class OrderViewModel: ObservableObject {
    @Published var state: OrderState = .idle
    func placeOrder() { /* logic nghiệp vụ */ }
}

Lựa chọn kiến trúc phụ thuộc vào quy mô nhóm, phạm vi dự án và khả năng kiểm thử yêu cầu. Đối với nhóm 1–2 nhà phát triển và dự án lên đến 20 màn hình, MVVM hoạt động tốt. Đối với nhóm lớn từ 5+ nhà phát triển và dự án có 50+ màn hình — Clean Architecture với cấu trúc mô-đun. MVC vẫn còn phù hợp để hiểu sự tiến hóa của kiến trúc, bảo trì dự án kế thừa và cho các màn hình UIKit đơn giản không có logic nghiệp vụ phức tạp.

Câu hỏi thường gặp

Vấn đề chính của MVC trong phát triển di động là gì?

Vấn đề chính là Massive View Controller. Trên iOS, UIViewController xử lý mọi thứ: xử lý đầu vào, cập nhật View, mạng, điều hướng và vòng đời. Trên Android, Activity/Fragment thực hiện các chức năng tương tự. Kết quả là, bộ điều khiển phình to đến hàng nghìn dòng mã, trở nên khó kiểm thử và bảo trì, vi phạm nguyên tắc đơn trách nhiệm.

MVC khác MVVM như thế nào?

Trong MVC, bộ điều khiển trực tiếp cập nhật View và xử lý đầu vào người dùng. Trong MVVM, vai trò bộ điều khiển được thực hiện bởi ViewModel, không có tham chiếu đến View — dữ liệu được truyền qua cơ chế liên kết. MVVM dễ kiểm thử hơn vì ViewModel không phụ thuộc vào UIKit hoặc Android Framework. Apple khuyến nghị MVVM với SwiftUI, Google khuyến nghị MVVM với Jetpack Compose.

Có thể sử dụng MVC trong các dự án hiện đại không?

Có, MVC vẫn là một mẫu hoạt động cho các màn hình đơn giản và nguyên mẫu. Apple khuyến nghị MVC cho các ứng dụng UIKit với màn hình đơn giản. Đối với các dự án phức tạp với nhiều màn hình, yêu cầu mạng và lưu cache, tốt hơn nên chọn MVVM, VIPER hoặc Clean Architecture. Các nhà phát triển mới bắt đầu nên thành thạo MVC trước khi học các mẫu phức tạp hơn.

Làm thế nào để kiểm thử ứng dụng MVC?

Model được kiểm thử riêng lẻ — đây là các đối tượng dữ liệu và logic nghiệp vụ thông thường. Controller khó kiểm thử do phụ thuộc vào UIKit hoặc Android Framework. Nên trích xuất logic nghiệp vụ khỏi bộ điều khiển vào các dịch vụ hoặc interactor riêng biệt, được kiểm thử bằng kiểm thử đơn vị. View thường không được kiểm thử bằng kiểm thử đơn vị — kiểm thử UI và kiểm thử ảnh chụp màn hình được sử dụng.

Nên chọn mẫu nào sau MVC?

Trên iOS — MVVM với SwiftUI và Combine, tiêu chuẩn của Apple từ năm 2019. Trên Android — MVVM với LiveData hoặc StateFlow, được Google chính thức khuyến nghị. Đối với các dự án lớn với nhóm từ 5+ nhà phát triển — Clean Architecture với VIPER trên iOS hoặc Clean Architecture trên Android với phân chia mô-đun theo tính năng. Đối với các dự án kế thừa với MVC — tái cấu trúc dần dần với việc trích xuất logic vào các dịch vụ riêng biệt.

Tổng kết

  • MVC — mẫu kiến trúc với sự phân tách thành Model, View và Controller
  • iOS MVC — UIViewController + storyboard + dịch vụ dữ liệu
  • Android MVC — Activity/Fragment + XML layout + kho lưu trữ
  • Massive View Controller — vấn đề chính do trộn lẫn trách nhiệm
  • Kiểm thử — Model dễ kiểm thử, Controller yêu cầu trích xuất logic
  • Tiến hóa — MVC → MVVM → Clean Architecture cho các dự án đang phát triển
  • Tương thích — có thể kết hợp các mẫu trong cùng một dự án

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm