Hiểu Rõ 7 Tầng Kiến Trúc WordPress: Model, Controller, Action, Service, Repository, Routes và Hook Là Gì?
Kiến trúc WordPress là nền tảng giúp lập trình viên tổ chức code plugin một cách chuyên nghiệp. Khi dự án trở nên phức tạp như CRM hay eCommerce, áp dụng kiến trúc WordPress đa tầng (Layered Architecture) là cách hiệu quả để tách biệt logic, dữ liệu và hiển thị, giúp code dễ bảo trì và mở rộng.
Nếu bạn chưa quen với cấu trúc plugin, hãy đọc thêm bài File entry point trong plugin WordPress để hiểu cách khởi tạo plugin đúng chuẩn.
Giới thiệu: Vì sao cần kiến trúc Layered trong WordPress
Khi xây dựng một plugin WordPress đơn giản, việc viết code trong một file duy nhất hoặc sử dụng các hook cơ bản là đủ. Nhưng khi dự án của bạn trở nên phức tạp – như một hệ thống CRM, một cổng quản lý dự án, hoặc một plugin e-commerce – cách tiếp cận đó sẽ nhanh chóng dẫn đến “code spaghetti” (code rối). Đây là lúc áp dụng một kiến trúc plugin WordPress đa tầng (Layered) phát huy tác dụng.
Mặc dù WordPress không áp dụng mô hình MVC (Model-View-Controller) nghiêm ngặt như các framework hiện đại (Laravel, Symfony), chúng ta hoàn toàn có thể triển khai Kiến trúc Layered (Đa tầng).
🎯 Việc này giúp phân tách rõ ràng các mối quan tâm (Separation of Concerns), giúp code của bạn:
- Dễ bảo trì (Maintainable): Sửa lỗi ở một nơi không làm hỏng 10 nơi khác.
- Dễ kiểm thử (Testable): Có thể viết Unit Test cho logic nghiệp vụ mà không cần “load” toàn bộ WordPress.
- Dễ mở rộng (Scalable): Thêm tính năng mới mà không cần viết lại toàn bộ logic.
🧩 Sơ Đồ Kiến Trúc WordPress Theo Mô Hình Layered
Mục tiêu của kiến trúc này là phân tách rõ ràng: Dữ liệu, Logic truy xuất dữ liệu, Logic nghiệp vụ, và Logic hiển thị.
Một luồng yêu cầu (request) trong kiến trúc này sẽ đi như sau:

Và WordPress Hooks (Action/Filter) chính là “chất kết dính” cho phép các tầng tương tác và mở rộng lẫn nhau.
👉 Mục tiêu: tách từng phần thành lớp riêng biệt để code rõ ràng, dễ kiểm soát và dễ mở rộng về sau.
Phân Tích Các Tầng (Layers) Trong Kiến Trúc WordPress Plugin
Hãy “mổ xẻ” từng thành phần trong kiến trúc này.
🧠 1. Model – Lớp Thực Thể (Entity Layer)
Model là một lớp PHP “ngu ngốc” (dumb class) hay POPO (Plain Old PHP Object).
- Chức năng: Đại diện cho cấu trúc dữ liệu của bạn, ví dụ: một bản ghi (row) trong
custom tablehoặc dữ liệu của mộtCustom Post Type. - Quy tắc: Model chỉ chứa thuộc tính (properties) và các hàm
get/setcơ bản. Nó không chứa logic truy vấn CSDL hay nghiệp vụ.
Ví dụ (Dữ liệu dự án thực tập):
<?php
// models/InternProject.php
class InternProject {
public $id;
public $name;
public $description;
public $mentor_id;
public $created_at;
// (Có thể thêm constructor hoặc các hàm getter/setter)
}
2. Repository – Lớp Truy Xuất Dữ Liệu (Data Access Layer)
Đây là tầng duy nhất được phép nói chuyện trực tiếp với Cơ sở dữ liệu (Database).
- Chức năng: Trừu tượng hóa các truy vấn CSDL (CRUD – Create, Read, Update, Delete). Nó nhận yêu cầu từ
Service(ví dụ:get_project_by_id(5)) và trả vềModelhoặc mộtarraycácModel. - Công cụ: Thường sử dụng
$wpdb(cho custom table) hoặcWP_Query(cho Post Type). - Lợi ích: Tách biệt hoàn toàn logic SQL. Nếu bạn muốn đổi từ
WP_Querysang$wpdbhoặc thêmCaching(ví dụ:WP_Object_Cache), bạn chỉ cần sửa ởRepository.
Ví dụ:
<?php
// repositories/InternProjectRepository.php
class InternProjectRepository {
private $wpdb;
private $table_name;
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
$this->table_name = $this->wpdb->prefix . 'intern_projects';
}
/**
* Lấy dự án bằng ID và trả về Model.
* @return InternProject|null
*/
public function find($id) {
$sql = $this->wpdb->prepare("SELECT * FROM {$this->table_name} WHERE id = %d", $id);
$result = $this->wpdb->get_row($sql, ARRAY_A);
if (!$result) {
return null;
}
// Map dữ liệu từ DB sang Model
$project = new InternProject();
$project->id = $result['id'];
$project->name = $result['name'];
// ...
return $project;
}
/**
* Lưu dự án (tạo mới hoặc cập nhật)
* @param InternProject $project
* @return int|bool
*/
public function save(InternProject $project) {
$data = [
'name' => $project->name,
'description' => $project->description,
'mentor_id' => $project->mentor_id,
];
if (empty($project->id)) {
// Tạo mới
$this->wpdb->insert($this->table_name, $data);
return $this->wpdb->insert_id;
} else {
// Cập nhật
return $this->wpdb->update($this->table_name, $data, ['id' => $project->id]);
}
}
}
3. Service – Lớp Xử Lý Nghiệp Vụ (Business Logic Layer)
Đây là “bộ não” của ứng dụng.
- Chức năng: Chứa toàn bộ logic nghiệp vụ chính. Nó điều phối các
Repositoryvà cácServicekhác để hoàn thành một tác vụ. - Ví dụ nghiệp vụ: “Khi tạo một dự án mới, hãy
validatedữ liệu, lưu vào CSDL (gọiRepository), sau đó gửi email thông báo chomentor(gọiEmailService), và ghi lạilog(gọiLogService).” - Quy tắc:
Servicekhông biết gì về HTTP (như$_POST) và cũng không biết gì về SQL. Nó chỉ nhận dữ liệu đầu vào, xử lý và trả về kết quả.
Ví dụ:
<?php
// services/InternProjectService.php
class InternProjectService {
private $project_repo;
private $email_service; // Một service khác được inject vào
public function __construct(InternProjectRepository $repo, EmailService $email) {
$this->project_repo = $repo;
$this->email_service = $email;
}
/**
* Tạo dự án mới với logic nghiệp vụ
* @param array $data Dữ liệu thô (đã được sanitize)
* @return int|WP_Error
*/
public function create_project($data) {
// 1. Validate nghiệp vụ
if (empty($data['name'])) {
return new WP_Error('validation_error', 'Tên dự án là bắt buộc.');
}
if (get_user_by('id', $data['mentor_id']) === false) {
return new WP_Error('validation_error', 'Mentor không tồn tại.');
}
// 2. Map data sang Model
$project = new InternProject();
$project->name = $data['name'];
$project->description = $data['description'];
$project->mentor_id = $data['mentor_id'];
// 3. Gọi Repository để lưu
$project_id = $this->project_repo->save($project);
if ($project_id) {
// 4. Thực thi nghiệp vụ phụ (gửi mail)
$this->email_service->send_new_project_notification($project_id, $data['mentor_id']);
}
return $project_id;
}
}
4. Action – Lớp Điều Phối Hành Động (Application Layer)
Action là lớp keo mỏng kết nối thế giới “lộn xộn” của HTTP (Form, AJAX) với thế giới “sạch sẽ” của Service.
- Chức năng: Xử lý các request cụ thể (như một form POST hoặc một AJAX call).
- Nhiệm vụ:
-
- Kiểm tra quyền (capability) và
nonce. Sanitizevàvalidatedữ liệu đầu vào (ví dụ:sanitize_text_field($_POST['name'])).- Lấy dữ liệu đã làm sạch và gọi
Servicetương ứng. - Xử lý kết quả trả về từ
Service(ví dụ:wp_redirecthoặcwp_send_json_success).
- Kiểm tra quyền (capability) và
Ví dụ:
<?php
// actions/CreateProjectAction.php
class CreateProjectAction {
private $service;
public function __construct(InternProjectService $service) {
$this->service = $service;
}
/**
* Hàm này được hook vào 'admin_post_create_project'
*/
public function handle() {
// 1. Kiểm tra Nonce và Quyền
if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'create_project_action')) {
wp_die('Bảo mật không hợp lệ!');
}
if (!current_user_can('manage_options')) {
wp_die('Không có quyền!');
}
// 2. Sanitize dữ liệu
$data = [
'name' => sanitize_text_field($_POST['project_name']),
'description' => sanitize_textarea_field($_POST['project_desc']),
'mentor_id' => intval($_POST['mentor_id']),
];
// 3. Gọi Service
$result = $this->service->create_project($data);
// 4. Xử lý kết quả
if (is_wp_error($result)) {
// Quay lại form với thông báo lỗi
wp_redirect(admin_url('admin.php?page=my-plugin-page&error=' . $result->get_error_code()));
} else {
// Thành công
wp_redirect(admin_url('admin.php?page=my-plugin-page&success=1'));
}
exit;
}
}
5. Controller – Lớp Điều Hướng (Presentation Layer)
Controller trong kiến trúc này là một lớp “mỏng”. Nó không chứa nghiệp vụ, không truy cập CSDL.
Chức năng: Chịu trách nhiệm đăng ký các Routes, Hook, Admin Menu, và render view (tải file template) để hiển thị HTML.
Nó là “bộ chỉ huy” thiết lập mọi thứ.
Ví dụ:
<?php
// controllers/ProjectPageController.php
class ProjectPageController {
private $create_action; // Action được inject vào
public function __construct(CreateProjectAction $action) {
$this->create_action = $action;
}
/**
* Đăng ký các hook cần thiết
*/
public function register() {
// 1. Đăng ký trang admin
add_action('admin_menu', [$this, 'add_menu_page']);
// 2. Đăng ký route (hook) cho form POST
// 'admin_post_create_project' sẽ gọi hàm 'handle' của 'CreateProjectAction'
add_action('admin_post_create_project', [$this->create_action, 'handle']);
}
public function add_menu_page() {
add_menu_page(
'Quản lý Dự án',
'Dự án Intern',
'manage_options',
'my-plugin-page',
[$this, 'render_page'] // Hàm render view
);
}
/**
* Render trang HTML (View)
*/
public function render_page() {
// Hàm này chỉ nên include file template
// Dữ liệu cần thiết cho view (nếu có) sẽ được lấy từ Repository
// ví dụ: $projects = $this->project_repo->get_all();
include MY_PLUGIN_PATH . 'views/admin-page-view.php';
}
}
6. Routes – Lớp Định Tuyến (Routing Layer)
Như đã thấy ở trên, WordPress không có một file routes/web.php tập trung. “Route” trong WordPress được định nghĩa bằng các Hook.
- Form Admin: Dùng
add_action('admin_post_{action_name}', $callback). - AJAX: Dùng
add_action('wp_ajax_{action_name}', $callback). - REST API: Dùng
register_rest_route($namespace, $route, $args).
Controller là nơi lý tưởng để đăng ký các route này.
7. Hook – Lớp Sự Kiện (Event Layer)
Hook (Action và Filter) là “linh hồn” của WordPress. Trong kiến trúc này, chúng ta dùng hook không chỉ để kết nối với lõi WordPress mà còn để giao tiếp giữa các tầng.
Ví dụ: Service của bạn có thể cung cấp một hook tùy chỉnh để các plugin khác mở rộng:
// Bên trong InternProjectService::create_project()
$data = apply_filters('my_plugin_before_create_project', $data);
// ... lưu project ...
do_action('my_plugin_after_create_project', $project_id);
Lợi Ích Khi Áp Dụng Kiến Trúc WordPress Đa Tầng
| Ưu điểm | Giải thích |
| Tách biệt rõ ràng (SoC) | Bạn biết chính xác nơi để tìm code. Lỗi CSDL? Vào Repository. Lỗi nghiệp vụ? Vào Service. Lỗi hiển thị? Vào View hoặc Controller. |
| Dễ Test (Testability) | Đây là lợi ích lớn nhất! Bạn có thể viết Unit Test cho InternProjectService mà không cần CSDL, bằng cách “mock” (giả lập) InternProjectRepository. |
| Tái sử dụng (Reusability) | Cùng một InternProjectService có thể được gọi bởi một Action (Form Post), một AJAX Handler và một REST API Endpoint. Logic nghiệp vụ được giữ ở một nơi duy nhất. |
| Dễ bảo trì & mở rộng | Muốn thay đổi từ Custom Table sang Custom Post Type? Bạn chỉ cần viết lại Repository mà Service và Controller không hề hay biết. |
| Bảo mật tập trung | Logic sanitize và validate được xử lý tập trung tại Action (cho request) và Service (cho nghiệp vụ), giảm thiểu rủi ro bỏ sót. |
Khi Nào Nên Sử Dụng Kiến Trúc Này?
Đây là một kiến trúc mạnh mẽ, nhưng cũng là “overkill” (quá phức tạp) cho một plugin đơn giản. Hãy sử dụng nó khi:
- Xây dựng plugin phức tạp: Các hệ thống quản lý (CRM, LMS, ERP, Quản lý dự án, Đặt chỗ…).
- Cung cấp REST API: Cần tách biệt logic nghiệp vụ khỏi logic của API endpoint.
- Dự án cần bảo trì lâu dài: Khi bạn biết dự án sẽ phát triển và có nhiều người cùng làm việc.
- Tích hợp hệ thống bên ngoài: Khi cần đồng bộ dữ liệu với AI, CRM, hoặc các dịch vụ bên thứ ba.
Kết Luận: Nâng Tầm Kỹ Năng WordPress Developer
Áp dụng kiến trúc Layered không phải là làm phức tạp hóa WordPress. Đó là áp dụng các nguyên tắc Software Engineering (Kỹ thuật Phần mềm) đã được chứng minh vào một nền tảng linh hoạt.
Bằng cách tách biệt rõ ràng Model, Repository, Service và Controller, bạn không chỉ viết code “chạy được” mà còn viết code chuyên nghiệp, bền vững và sẵn sàng cho tương lai.
