+2

Series Cấu trúc Modules trong Laravel | Bài 2: Khởi tạo Module đầu tiên với nwidart/laravel-modules

Chào anh em, ở Bài 1 chúng ta đã cùng nhau "bóc phốt" những điểm yếu chí mạng của kiến trúc MVC mặc định khi ứng dụng phình to, đồng thời hiểu được tư tưởng chia để trị của Kiến trúc Modules.

Hôm nay, chúng ta sẽ bắt tay vào thực hành. Trong thế giới Laravel, để setup kiến trúc này, chúng ta có thể tự viết code thuần (bằng cách custom lại Service Providers và Composer autoloader). Tuy nhiên, nguyên tắc của một kỹ sư backend là "Không phát minh lại cái bánh xe". Package nwidart/laravel-modules đã làm việc này quá xuất sắc và trở thành tiêu chuẩn công nghiệp (industry standard).

Hãy cùng cài đặt và tạo ra Module đầu tiên nhé!

1. Cài đặt và cấu hình

Đầu tiên, mở terminal lên và kéo package về thông qua Composer:

composer require nwidart/laravel-modules

Tiếp theo, chúng ta cần publish file cấu hình của package ra ngoài để dễ dàng tùy biến:

php artisan vendor:publish --provider="Nwidart\Modules\LaravelModulesServiceProvider"

Lệnh này sẽ sinh ra một file config/modules.php. Mặc định, package sẽ cấu hình thư mục chứa các module của chúng ta là thư mục Modules nằm ở thư mục gốc (ngang hàng với thư mục app).

Lưu ý quan trọng cho anh em: Để Laravel hiểu được các class bên trong thư mục Modules mới này, bạn phải cập nhật lại file composer.json để thêm chuẩn autoload PSR-4:

{
  "autoload": {
    "psr-4": {
      "App\\": "app/",
      "Modules\\": "Modules/",
      "Database\\Factories\\": "database/factories/",
      "Database\\Seeders\\": "database/seeders/"
    }
  }
}

Sau đó chạy lệnh: composer dump-autoload để hệ thống cập nhật lại danh sách class.

2. Tạo Module đầu tiên: "Order"

Giả sử hệ thống của chúng ta cần một module để quản lý Đơn hàng. Thay vì phải hì hục tạo từng thư mục, package cung cấp sẵn một command cực kỳ quyền lực:

php artisan module:make Order

Bùm! Mở thư mục Modules/Order lên, bạn sẽ thấy một cấu trúc lộng lẫy được sinh ra sẵn:

Modules/Order/
├── Config/
├── Console/
├── Database/
│   ├── Migrations/
│   ├── Seeders/
├── Entities/       <-- (Chính là thư mục chứa Models)
├── Http/
│   ├── Controllers/
│   ├── Middleware/
│   ├── Requests/
├── Providers/      <-- (Trái tim của Module)
├── Resources/
│   ├── views/
├── Routes/
│   ├── api.php
│   ├── web.php
├── module.json     <-- (Định danh của Module)

Như bạn thấy, nó là một mini-framework nằm gọn bên trong Laravel. Mọi thứ từ Route, Controller, Model (Entities), cho đến Migration đều độc lập hoàn toàn.

3. Hiểu sâu dưới màng bọc: Tại sao Laravel đọc được Module này?

Với những anh em đam mê thiết kế hệ thống, chắc hẳn sẽ thắc mắc: "Thư mục Routes/api.php hay Migrations nằm tít trong Modules/Order, làm sao thằng lõi Laravel nó biết đường mà load?"

Bí mật nằm ở thư mục Providers. Khi bạn tạo module, package đã tự động sinh ra file Modules/Order/Providers/RouteServiceProvider.php và OrderServiceProvider.php.

Hãy nhìn vào hàm boot() của OrderServiceProvider.php, bạn sẽ thấy các đoạn code nạp cấu hình, nạp view và nạp migration:

$this->loadMigrationsFrom(module_path('Order', 'Database/Migrations'));
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);

Package nwidart đóng vai trò như một "nhà quản lý". Khi framework Laravel khởi động, nwidart sẽ quét qua tất cả các thư mục trong Modules/, đọc file module.json để xem module nào đang được active, sau đó nó sẽ gọi các Service Providers của module đó để tiêm (inject) Routes, Migrations, Views vào hệ thống lõi.

Đây chính là tư duy thiết kế "Plug and Play" (Cắm và chạy) cực kỳ kinh điển trong phần mềm!

4. Chạy thử Route đầu tiên

Mở file Modules/Order/Routes/api.php lên, bạn sẽ thấy một route mặc định đã được đăng ký:

Route::middleware('auth:api')->get('/order', function (Request $request) {
    return $request->user();
});

Lưu ý: Mặc định các route trong file này đã được package tự động prefix với tên module (ví dụ: /api/order/).

Bạn có thể tạo một controller mới chuyên biệt cho Module này:

php artisan module:make-controller OrderController Order

Mọi command quen thuộc của Laravel (make:model, make:migration, make:request) đều được package này hỗ trợ, chỉ cần thêm prefix module: và chỉ định tên Module ở cuối.

Tổng kết

Cài đặt kiến trúc Modules không hề khó, cái khó nhất là thay đổi tư duy gom nhóm code. Giờ đây, code của bạn đã được đóng gói gọn gàng. Nếu dự án có thêm Module Payment, bạn chỉ cần một lệnh make là xong, không đụng chạm đến bất kỳ file nào của Module Order.

Nhưng khoan đã! Ở bài toán thực tế, khi tạo một đơn hàng (Order), hệ thống phải trừ tiền (Payment). Vậy làm sao hai Module này giao tiếp với nhau mà không bị dính chặt vào nhau (High Coupling)?

Ở Bài 3, chúng ta sẽ giải quyết bài toán hóc búa này bằng việc thiết lập các Service Contracts và sử dụng kiến trúc Event-Driven (Gửi - Nhận sự kiện). Hẹn anh em ở bài sau!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí