🏢 Kiến trúc Multi-tenant SaaS & Cô lập Dữ liệu
LMS hoạt động theo mô hình Multi-tenant SaaS với ranh giới phân tách dữ liệu 2 cấp độ: Organization (Tổ chức) và Center (Chi nhánh / Cơ sở).
1. Mô hình Phân cấp Thực thể & Ràng buộc Khóa
Ràng buộc Khóa & Định danh
- Ràng buộc định danh người dùng:
UNIQUE(organization_id, username). Email là tùy chọn (nullable) và không bắt buộc duy nhất toàn cục. - Subdomain Routing: Tên miền con của request (ví dụ:
abc.localhost:8085hoặcabc.cerylms.com) sẽ tự động được phân giải thànhOrganization ID = abc.
2. Cơ chế Cô lập Dữ liệu trên Backend
2.1. TenantManager Static Service
Service quản lý Context của request hiện tại (Modules/Core/App/Services/TenantManager.php):
TenantManager::getOrganizationId()TenantManager::getCenterId()TenantManager::setCenterId($centerId)
2.2. Trait HasTenantScope
Mọi Model nghiệp vụ (Lớp học, Học viên, Hóa đơn, Giao dịch) BẮT BUỘC sử dụng trait HasTenantScope:
namespace Modules\Academic\App\Models;
use Illuminate\Database\Eloquent\Model;
use Modules\Core\App\Traits\HasTenantScope;
use Modules\Core\App\Traits\Auditable;
class AcademicClass extends Model
{
use HasTenantScope, Auditable;
protected $fillable = [
'organization_id',
'center_id',
'name',
'code',
'status',
];
}
Tác dụng của
HasTenantScope:
- Tự động thêm điều kiện
WHERE center_id = TenantManager::getCenterId()vào mọi câu lệnhSELECT,UPDATE,DELETE.- Tự động điền
center_idvàorganization_idkhi gọi phương thứcModel::create().
3. Kiến trúc Dual-Surface Routing qua Nginx Gateway
Nginx traffic routing (:8085):
/admin/*, /portal/* → Vite Dev Server (React SPA)
/api/*, /sanctum/* → Laravel PHP-FPM (RESTful API)
/* (Fallback) → Laravel Blade Engine (Landing Page công khai tối ưu SEO)
- Quy tắc: Tuyệt đối không thêm các trang HTML công khai (Landing Page, bài viết tin tức) vào React SPA. Mọi trang public cần SEO đều do Laravel Blade Engine kết hợp middleware
LpHostRouterMiddlewarexử lý.