Chiến lược Multi-Tenancy
Trạng thái: Draft · Ngày: 2026-05-22 · Chủ trì: Phat Nguyen (PM/PO) Quyết định kỹ thuật đi kèm: ADR-0001 Các mức cô lập multi-tenancy.
Trang này ghi lại chúng ta muốn gì ở multi-tenancy và vì sao. Phần làm như thế nào nằm trong ADR.
Bài toán
BANA phục vụ nhiều doanh nghiệp (Organizer) trên cùng một nền tảng. Hiện mọi tenant dùng chung service và chung một database (nx_seller), chỉ phân biệt bằng cột merchantId / organizerId lọc ở tầng ứng dụng — tức mô hình Pool. Khi số lượng tenant và độ đa dạng hợp đồng tăng lên, một mô hình duy nhất không còn hợp với tất cả:
- SMB đại trà cần chi phí/tenant rẻ nhất có thể.
- Khách lớn / enterprise có thể đòi cô lập dữ liệu, tài nguyên riêng, hoặc thậm chí deploy on-prem.
- Ta phải có khả năng di chuyển tenant giữa các mô hình khi nhu cầu thay đổi — gồm cả chiều khó: gộp một tenant riêng biệt trở về nền tảng chung.
Mục tiêu
Biến mức cô lập thành thuộc tính theo từng Organizer, quyết định lúc chạy — không phải lựa chọn cứng một lần cho cả hệ thống. Mặc định mọi tenant ở mức rẻ nhất; nâng cấp từng Org khi cần; vẫn giữ đường quay về.
Đơn vị cô lập (tenant boundary)
Đơn vị cô lập là Organizer (Org) — một Org cùng toàn bộ Merchant con (chi nhánh) di chuyển như một khối. organizerId là khóa phân vùng.
Ba mức (góc nhìn sản phẩm)
| Mức | Khách nhận được gì | Chi phí | Bán cho |
|---|---|---|---|
| POOL (mặc định) | Chung service + chung DB; tách biệt logic | Thấp nhất | SMB, đại trà |
| BRIDGE | Chung service + DB riêng mỗi Org | Trung bình | Khách cần cô lập dữ liệu |
| SILO | Service + DB riêng mỗi Org (namespace/stack riêng) | Cao nhất | Enterprise, on-prem, hợp đồng đặc thù |
Ánh xạ chuẩn ngành (AWS SaaS Lens): Pool / Bridge / Silo. BANA hiện đang chạy Pool.
Yêu cầu
| # | Yêu cầu | Mức |
|---|---|---|
| R1 | Org mới onboard tức thì vào POOL, không cần provision | POOL |
| R2 | Org nâng cấp POOL → BRIDGE → SILO mà không đổi URL client hay code nghiệp vụ | tất cả |
| R3 | Org hạ cấp SILO → POOL (gộp về) với dữ liệu merge an toàn | tất cả |
| R4 | Dữ liệu tenant mang ID toàn cục duy nhất để merge không bao giờ trùng | nền tảng |
| R5 | Org "ồn ào"/nặng ở mức cao không ảnh hưởng Org khác | BRIDGE, SILO |
| R6 | Một đường migrate schema chạy được cho mọi mức | nền tảng |
| R7 | Vệ sinh DB dài hạn: cleanup, split, merge có runbook lặp lại được | nền tảng |
Tiêu chí thành công
- [ ] Mức cô lập là một trường của Org, mặc định POOL.
- [ ] Di chuyển Org giữa các mức là runbook có tài liệu, không phải dự án riêng mỗi lần.
- [ ] Không thể trùng ID
merchantId/organizerIdkhi gộp SILO về POOL. - [ ] Thêm Org mới vào POOL không cần thao tác hạ tầng nào.
Câu hỏi mở (cần chốt trước khi Accepted)
| Câu hỏi | Trạng thái | Ảnh hưởng |
|---|---|---|
| Thị trường mục tiêu chính — SMB hay enterprise? | Nghiêng hybrid, còn thăm dò | Quyết định mức đầu tư SILO ngay bây giờ có đáng không |
| Ràng buộc compliance / data-residency / on-prem? | Chưa xác định | Nếu có → SILO + Helm chart portable thành bắt buộc, không còn tùy chọn |
| BRIDGE là mức cố định hay chỉ là trạm trung chuyển sang SILO? | Mở | Quyết định có xây trọn tooling cho BRIDGE không |
| Dùng Postgres Row-Level Security hay giữ lọc tầng app? | Mở | Tư thế bảo mật của mức POOL |
Ngoài phạm vi
- Thiết kế lại ca/cash-session (theo dõi riêng, đang bàn với nhân viên).
- Feature flag / tùy biến theo tenant ngoài phạm vi cô lập.
Thuật ngữ
| Thuật ngữ | Ý nghĩa |
|---|---|
| Org / Organizer | Tenant cấp cao nhất; chứa N Merchant |
| Merchant | Đơn vị kinh doanh / chi nhánh thuộc một Org |
| Tenant Registry | Bảng tra: orgId → { isolationTier, datasourceRef } |
| Connection Resolver | Lớp chọn đúng kết nối DB theo tenant lúc chạy |
| Snowflake ID | ID 64-bit toàn cục duy nhất; enabler để merge tenant an toàn |