Trang chủTác giảLiên hệ

MVC và những điều có thể bạn chưa biết.

By Cao Văn Thành
Published in General
June 06, 2021
8 min read

MVC là một pattern đầu tiên mà ai học về lập trình cũng đều biết không sớm thì muộn. Tuy nhiên để có thể vận dụng nó một cách chuẩn xác thì không có một phương án cụ thể nào cả mà hoàn toàn dựa trên quan điểm của người sử dụng. Chính vì thế, bài viết dưới đây viết về mô hình MVC theo quan điểm của người viết.

Khát quát chung

Các mô hình Framework, đặc biệt là của PHP thường được triển khai theo mô hình MVC. Vậy MVC là gì ?

MVC là mô hình gồm 3 thành phần Model - View - Controller, trong đó: Model : chứa các thành phần làm nhiệm vụ xử lý dữ liệu, chứa các business logic. Controller: chứa các thành phần làm nhiệm vụ điều hướng. View: chứa các thành phần làm nhiệm vụ hiển thị. ![alt text](https://kipalog.kaopiz.com/uploads/e0fa/42eb/Hình chụp từ 2017-05-13 05:08:53.png) Tại đây chúng ta thấy luồng dữ liệu đi từ View, gửi data cho Controller để Controller update model và lấy ra dữ liệu từ model để truyền lại cho View.

Nhiều người sẽ thấy mối liên kết giữa View và Model có vẻ hơi lạ vì trước giờ hiếm có khi nào thấy sử dụng. Đó là vì thực chất chúng ta đang sử dụng mô hình MVP, một dẫn xuất của MVC. P (presenter) trong mô hình MVP có nhiệm vụ tách biệt M và V để tránh phụ thuộc vào nhau. Còn về bản chất trong MVC, M và V vẫn có liên kết dữ liệu với nhau. Trong trường hợp View và Controller có liên hệ trực tiếp với nhau theo kiểu 2-ways binding, chúng ta sẽ có dẫn xuất khác, đó là MVVM.

OK. Dù tên gọi có là gì thì nội dung chủ yếu của MVC vẫn là như vậy.

#Triển khai MVC

Có thể nhiều người tự hỏi: “Ơ, khi làm việc với các framework thì nó đã chia sẵn các thành phần cho mình, việc gì mình phải làm việc đó? “. Thực ra, các FW sẽ định hình sẵn các nơi chứa MVC thôi, còn việc triển khai MVC vào dự án cụ thể lại có thể tùy biến khác nhau.

Phân bổ controller

Mô hình phân bổ cơ bản ![alt text](https://kipalog.kaopiz.com/uploads/dba5/06df/Hình chụp từ 2017-05-13 05:47:50.png) Mô hình này chúng ta rất hay gặp. Tuy nhiên có các vấn đề xảy ra như sau:

  1. Server phải giải quyết tất cả các công việc trong khi phía client chỉ render view và không làm gì -> lãng phí tài nguyên.
  2. Mỗi khi có action, bên Server phải lặp lại các thao tác y hệt như trước và truyền tải lại cho phía client -> nghẽn băng thông.

Hướng giải quyết

  1. Cần đẩy bớt công việc cần làm cho phía client.
  2. Chỉ tải những thay đổi cần thiết.

Và mô hình phân bổ của chúng ta sẽ trở thành: ![alt text](https://kipalog.kaopiz.com/uploads/a844/696b/Hình chụp từ 2017-05-13 06:07:41.png)

Ở mô hình này, nếu server chỉ gửi template 1 lần đầu tiên, còn lại đều tương tác qua 2 Controller 2 phía Server và Client, chúng ta sẽ có mô hình dạng Single Web Page hay được sử dụng trong MEAN Stack (Mongo-Express-Angular-NodeJS) hay MERN Stack(Mongo-Express-React-NodeJS). Tuy nhiên, thực tế có thể sử dụng bất kỳ loại FW/Công nghệ nào phía server và bất kỳ FW/công nghệ nào phía Client để triển khai cho phù hợp.

Khi tách logic ra làm 2 phía Client và Server, điều gì sẽ quyết định logic này được viết ở đâu, logic kia được viết ở đâu ? Để biết được điều này, chúng ta xem xét tới đặc điểm của 2 phía Client và Server.

Server: Lưu trữ tại 1 nơi nào đó, thực hiện những gì không ai biết, tài nguyên xử lý có hạn với nhiều người truy cập, dung lượng lưu trữ tốt. Xử lý các tác vụ phức tạp nhưng thời gian có thể chậm. Client: Lưu trữ khắp nơi, ai cũng biết, cũng thấy, đối với 1 người dùng thì tài nguyên sử dụng tương đối thoải mái, dữ liệu lưu trữ với dung lượng thấp. Xử lý các tác vụ nhanh và đơn giản.

Vậy đó chính là vấn đề về bảo mật. Ở bài viết này không nói quá nhiều về vấn đề bảo mật, nhưng khi phân bổ Controller, chúng ta cần phân định rõ ràng các logic cần thiết để chia tách cho hợp lý.

  1. Các vấn đề về hiển thị, đại đa số được xử lý ở phía Client.
  2. Các vấn đề xử lý quyền truy cập phân định ở phía Server.
  3. Các vấn đề về chỉnh sửa dữ liệu dẫn xuất từ dữ liệu thô thường được phân định ở phía Client (Trong trường hợp dữ liệu để xử lý quá lớn, quá chậm ở bất kỳ phía nào có thể lưu trữ dữ liệu dẫn xuất trên server). Tuy nhiên trong trường hợp muốn giấu công thức xử lý dữ liệu có thể viết ở phía Server.
  4. Validate dữ liệu trước khi send tại Server mặc dù trong thực tế thường được xử lý ở phía Client tuy nhiên việc này không thực sự bảo mật do người sử dụng có thể chỉnh sửa code phía client để send request cho server, cần có 1 cơ chế bảo mật phía Client.

Phân bổ model

Tương tự như Controller, model cũng cần được phân bổ hợp lý. Ở các mô hình truyền thống, phía Client nói chung không có model, không lưu trữ data mà chỉ request dữ liệu từ phía server. Tuy nhiên một số mô hình mới có thể xuất hiện thêm phần model ở phía Client giúp giảm tải model ở phía Server. Mô hình sẽ như sau:

![alt text](https://kipalog.kaopiz.com/uploads/884d/eec2/Hình chụp từ 2017-05-13 06:57:37.png)

Phần model phía Client được hình thành với mục đích là một phần dữ liệu để truy xuất, lưu trữ dữ liệu tạm thời phía Client để theo dõi lượt chỉnh sửa cuối, và sau một thời gian thì đồng bộ với Model phía Server. Hiện tại, rất ít hệ thống tổ chức theo mô hình như này vì nó có vẻ làm phức tạp hóa phía Client hơn mà hiệu quả cũng không được cao.

Khi sử dụng model phía Client để chỉnh sửa hay cập nhật dữ liệu, rất dễ dẫn tới tình trạng truy xuất quá quyền hạn của mình, thay đổi các dữ liệu trên Model phía Client. Để tránh tình trạng đó, Model phía Server khi update/sync từ các Client cần cẩn trọng trong việc xem xét quyền hạn của các Client.

#MVC chi tiết

Model

Model là nơi chứa các class/hàm, … liên quan tới dữ liệu và xử lý tiền dữ liệu. Thông thường trong model sẽ gồm có:

  • Các lớp thể hiện dữ liệu của 1 record(Entity/Row/Record/… trong DB tùy Design): Tại đây lưu trữ các thông tin về dữ liệu, các dữ liệu dẫn xuất từ DB, các hàm cơ bản cho việc thao tác với một record/row/entity/…
  • Các lớp thể hiện dữ liệu của 1 bảng và các tương tác với bảng đó(DAO/Table/… tùy Design): Tại đây lưu trữ các truy xuất cơ bản đối với dữ liệu. Việc tối ưu truy vấn nằm chủ yếu tại đây. Khi sử dụng các fw tại lớp này, cần tránh sử dụng những hàm cơ bản của fw mà nên viết 1 lớp trung gian giữa các thành phần. Việc này không mấy hữu dụng trong ngắn hạn, tuy nhiên khi cần thiết phải nâng cấp phiên bản, chúng ta sẽ không phải mất quá nhiều công sức vào việc việc thay đổi toàn bộ lớp này.
  • Các lớp liên kết DB bên ngoài (Connection/ … ): Lưu trữ các thông tin về kết nối liên kết tới database. Nên sử dụng Singleton để thiết kế lớp này. Vì bài viết không đi sâu vào Design Pattern nên chỉ đưa ra các từ khóa để chúng ta tự tìm hiểu.

Ngoài ra, trong một số hệ thống có thể có thêm các thành phần khác như:

  • Các kích hoạt kèm theo (Trigger/Behavior/… ).
  • Các lớp xử lý logic tiền dữ liệu (Business Logic/ Process… ): Đối với những bài toán nghiệp vụ phức tạp thường có tầng này kèm theo.
  • Các lớp logic sử dụng chung.

![alt text](https://kipalog.kaopiz.com/uploads/c440/e2ad/Hình chụp từ 2017-05-26 09:39:32.png)

Trên đây là một mô hình đơn giản về Model, một số fw có thể không theo mô hình trên và cũng không nhất thiết phải đầy đủ các thành phần. Suy cho cùng thì mô hình cũng chỉ là những gì mà chúng ta tạo nên, không nhất thiết phải có một mô hình cụ thể nào cả, hãy biết cách vận dụng cho hợp lý và phù hợp.

###Controller Thông thường Controller có ít tác vụ. Các nhiệm vụ của Controller thông thường gồm nhận request, gửi request tới Model, lấy dữ liệu từ Model, request tới View, render View. Do đó, Controller hiệu quả khi tối thiểu xử lý dữ liệu (trong mô hình MVC đối với web, điều này có thể không chính xác khi thiết kế App). Chính vì thế, Controller thường không được chia ra thành phần con.

Router: Router là một bộ phận đặc biệt của Controller khi thiết kế web, do đó các fw thường tách Router ra trở thành một bộ phận riêng. Router có nhiệm vụ điều khiển url request, map url request của người dùng với hàm xử lý tương ứng.

View

View làm nhiệm vụ hiển thị dữ liệu. Đối với web, View có thể chia ra thành:

  • Layout: chứa các khung định hình cấu trúc webpage.
  • Template: chứa các mã html, một phần hoặc toàn bộ trang. Việc chia trang web thành các template thế nào hoàn toàn phụ thuộc vào người viết.
  • Logic View: xử lý các thông tin hiển thị nhận được từ Controller. Khi có điều kiện để hiển thị dữ liệu, chúng ta nên xử lý tại đây chứ không nhúng logic view code vào Templates.
  • Static assets: Đây là nơi lưu trữ các assets cho hệ thống như các file js, css, html, image, … khi làm việc, thường chúng ta không mấy quan tâm đến thành phần này. Tuy nhiên khi deploy lên server thật, cần thiết lập quyền cho các thư mục rõ ràng và chi tiết để tránh bị tấn công, đặc biệt khi có chức năng upload file.

Ngoài ra, các fw thường có cung cấp thêm thành phần Helper để hiển thị một số thành phần cơ bản trên view. Tuy nhiên nên sử dụng một lớp trung gian cho các Helper cung cấp cơ bản để tiện cho việc maintain/upgrade sau này.

![alt text](https://kipalog.kaopiz.com/uploads/5394/d2c2/Hình chụp từ 2017-05-26 10:35:33 - 1.png)

Miscellaneous

Một số fw hiện đại thường có các thành phần con làm hoàn chỉnh một số nhiệm vụ cơ bản như Cell/Element/plugin/… Mỗi thành phần này có đầy đủ hoặc một phần Model - View - Controller. Đây là tiền đề của mô hình HMVC (Hierarchy MVC). Tuy nhiên do bản thân các fw đã là MVC nên việc chia thành các thành phần con thường chỉ với mục đích viết các thư viện dùng chung cho cộng đồng chứ ít khi được sử dụng vào một hệ thống cụ thể. Các fw/platform hay bất cứ thứ gì không theo MVC từ đầu có thể sử dụng HMVC một cách dễ dàng hơn (tùy theo khả năng phân chia của Software Architechturer).

---------------------------------------------------------------------------------------------------------------------------- Bài viết dựa trên hiểu biết và một số ý kiến cá nhân của người viết, có thể có những quan điểm khác biệt, hoặc nếu nhận thấy trong bài viết có phần chưa chính xác xin hãy để lại thông tin để người viết chỉnh sửa.


Tags

MVCFrameworkModelViewController

Cao Văn Thành

Mình đã làm việc với web được 1 thời gian và mình thích chia sẻ về công nghệ. Hy vọng chúng ta sẽ xây dựng được một cộng đồng vững mạnh cho thế hệ Dev GenZ tại Việt Nam.

Related Posts

[Review code] Trách nhiệm đến từ hai phía (P1)
October 10, 2021
5 min
© 2021, All Rights Reserved.

Quick Links

Liên hệ quảng cáoThông tinLiên hệ

Social Media