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:

Sơ đồ kiến trúc Layered trong WordPress gồm Route, Controller, Action, Service, Repository, Model và Hook
Sơ đồ kiến trúc Layered trong WordPress gồm Route, Controller, Action, Service, Repository, Model và Hook

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 table hoặc dữ liệu của một Custom Post Type.
  • Quy tắc: Model chỉ chứa thuộc tính (properties) và các hàm get/set cơ 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ề Model hoặc một array các Model.
  • Công cụ: Thường sử dụng $wpdb (cho custom table) hoặc WP_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_Query sang $wpdb hoặc thêm Caching (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 Repository và các Service khá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 validate dữ liệu, lưu vào CSDL (gọi Repository), sau đó gửi email thông báo cho mentor (gọi EmailService), và ghi lại log (gọi LogService).”
  • Quy tắc: Service khô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ụ:
    1. Kiểm tra quyền (capability) và nonce.
    2. Sanitizevalidate dữ liệu đầu vào (ví dụ: sanitize_text_field($_POST['name'])).
    3. Lấy dữ liệu đã làm sạch và gọi Service tương ứng.
    4. Xử lý kết quả trả về từ Service (ví dụ: wp_redirect hoặc wp_send_json_success).

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 RepositoryServiceController không hề hay biết.
Bảo mật tập trung Logic sanitizevalidate đượ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:

  1. 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ỗ…).
  2. Cung cấp REST API: Cần tách biệt logic nghiệp vụ khỏi logic của API endpoint.
  3. 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.
  4. 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.