Chuyển tới nội dung chính
Tác giả
taihv - Hồ Văn Tài
taihv - Hồ Văn TàiCustomer Success Engineer (Intern Beta)
Biên tập
Phuc Pham
Phuc PhamBiên tập viên

Chuyển đổi kiến trúc dữ liệu NoSQL (mongodb) từ chia sẻ lược đồ điều khiển nhiều bên thuê thành chia sẻ lược đồ nhiều bên thuê

I. Benefits

  • Chuẩn hóa cách tổ chức dữ liệu cho nhiều doanh nghiệp trên cùng hệ thống.
  • Giảm chi phí vận hành khi số lượng doanh nghiệp tăng.
  • Cho phép mở rộng trường nghiệp vụ mà không làm hỏng dữ liệu cũ.
  • Bảo đảm Web và Mobile dùng chung dữ liệu và cùng một mô hình nghiệp vụ.
  • Tạo nền tảng để mở rộng Qranty V2 có kiểm soát.

II. Prerequisites

Người đọc cần có kiến thức cơ bản về:

  • MongoDB document và collection.
  • Database schema và schemaless.
  • Kiến trúc multi-tenant.
  • Index, partition và migration dữ liệu.
  • Nghiệp vụ hiện tại của Qranty.
  • Quyền truy cập tài liệu kỹ thuật nội bộ Qranty.

III. Information Level

  • infoLevel:3 (reader must be signed official employee's contract from at least junior grade)
  • writingLevel:2 (text with diagrams and technical tables)

IV. Architecture Decision Record

1. Trạng thái

Proposed — Đề xuất để Kỹ sư trưởng và đội kỹ thuật xem xét, phê duyệt.

2. Bối cảnh

Qranty cần phục vụ nhiều doanh nghiệp trên cùng hệ thống. Kiến trúc hiện tại gắn chặt dữ liệu vào từng model, làm tăng chi phí thay đổi cấu trúc, vận hành và mở rộng khi số lượng doanh nghiệp tăng.

Thiết kế mới cần:

  • Cách ly dữ liệu giữa các doanh nghiệp.
  • Cho phép Web và Mobile sử dụng chung dữ liệu.
  • Hỗ trợ thêm trường nghiệp vụ có kiểm soát.
  • Mở rộng dung lượng mà không tạo database riêng cho từng doanh nghiệp.
  • Có quy trình migration và rollback an toàn.

3. Các phương án được xem xét

Phương án A: Một database cho mỗi tenant

Ưu điểm:

  • Cách ly dữ liệu rõ ràng.
  • Dễ sao lưu riêng từng tenant.

Hạn chế:

  • Số database, connection và tác vụ vận hành tăng theo số tenant.
  • Khó triển khai đồng nhất khi thay đổi cấu trúc.

Phương án B: Một collection cho mỗi tenant

Ưu điểm:

  • Dữ liệu được tách tương đối rõ.
  • Truy vấn một tenant đơn giản.

Hạn chế:

  • Số collection tăng nhanh.
  • Khó quản lý index, migration và giám sát.

Phương án C: Collection dùng chung có tenantId

Ưu điểm:

  • Một bộ index và một quy trình nâng cấp cho tất cả tenant.
  • Chi phí vận hành thấp hơn.
  • Thuận lợi cho Web và Mobile sử dụng chung API.

Hạn chế:

  • Mọi truy vấn bắt buộc phải lọc theo tenantId.
  • Sai sót ở lớp truy cập dữ liệu có thể gây truy cập chéo tenant.

4. Quyết định

Qranty V2 sử dụng:

  • Một MongoDB database dùng chung cho nhiều doanh nghiệp.
  • Mọi document nghiệp vụ bắt buộc có tenantId.
  • Không tạo database hoặc collection theo tên doanh nghiệp.
  • Dữ liệu được phân thành 5 nhóm vận hành.
  • Document gồm systemData và entityData.
  • entityData được phép linh hoạt nhưng phải có hợp đồng dữ liệu và phiên bản.
  • Collection được mở rộng bằng partition khi đạt ngưỡng vận hành.

4.1. Sơ đồ kiến trúc tổng thể

Kiến trúc tổng thể Qranty V2

Hình 1 — Web và Mobile dùng chung API; API xác định tenant, định tuyến dữ liệu qua ba collection kỹ thuật và năm họ collection nghiệp vụ.

Luồng chính trong sơ đồ:

  1. Web hoặc Mobile gửi request tới Qranty V2 API.
  2. API lấy tenantId từ ngữ cảnh xác thực, không tin giá trị tenant do client tự gửi.
  3. API kiểm tra quyền, hợp đồng dữ liệu và tra registry để xác định đúng LV/partition.
  4. MongoDB lưu dữ liệu nhiều tenant trong cùng database, nhưng mọi document nghiệp vụ đều được phân tách logic bằng systemData.tenantId.
  5. Bộ nhớ đệm chỉ dùng để tăng tốc; cache key vẫn phải chứa tenantId.

5. Cấu trúc document

Qranty V2 sử dụng cấu trúc document thống nhất cho toàn bộ dữ liệu nghiệp vụ.

Mỗi document gồm:

  • _id: định danh duy nhất của document.
  • systemData: thông tin chung do hệ thống kiểm soát.
  • entityData: thông tin nghiệp vụ theo từng loại dữ liệu.

Cấu trúc này giúp hệ thống vừa duy trì được các trường bắt buộc, vừa cho phép mở rộng dữ liệu nghiệp vụ mà không phải thay đổi toàn bộ collection.

Cấu trúc document hai khối

Hình 2 — Một document gồm khối systemData cố định để kiểm soát tenant, phiên bản và định tuyến; khối entityData linh hoạt theo nghiệp vụ.

5.1. Khối systemData

systemData trả lời các câu hỏi:

  • Document thuộc doanh nghiệp nào?
  • Document thuộc LV nào?
  • Document là loại dữ liệu gì?
  • Document nằm trong partition nào?
  • Document đang sử dụng phiên bản cấu trúc nào?
  • Document được tạo và cập nhật bởi ai?

Các trường chính:

TrườngBắt buộcÝ nghĩa
tenantIdDoanh nghiệp sở hữu document
levelCấp dữ liệu từ LV1 đến LV5
entityTypeLoại dữ liệu nghiệp vụ
partitionIdĐịnh danh partition logic
systemSchemaVersionPhiên bản cấu trúc của systemData
entitySchemaVersionPhiên bản cấu trúc của entityData
recordVersionTăng sau mỗi lần cập nhật để chống ghi đè
recordStatusTrạng thái active, deleted hoặc archived
createdAtThời điểm tạo document
createdByNgười dùng hoặc tiến trình tạo document
updatedAtThời điểm cập nhật gần nhất
updatedByNgười dùng hoặc tiến trình cập nhật gần nhất
deletedAtKhi xóa mềmThời điểm đánh dấu xóa
deletedByKhi xóa mềmNgười hoặc tiến trình thực hiện xóa

Tên collection vật lý không được lưu trực tiếp trong document.

Document chỉ lưu partitionId. Hệ thống sử dụng system_partitions để ánh xạ partitionId sang collection vật lý tương ứng.

Cách này cho phép hệ thống đổi tên, đóng hoặc mở partition mà không phải cập nhật toàn bộ document cũ.

5.2. Khối entityData

entityData chứa các trường nghiệp vụ riêng của từng entityType.

Ví dụ:

  • customercustomerName, customerPhone, customerEmail, birthDateaddress.
  • productItemserialNumber, productStatus và thông tin tham chiếu sản phẩm.
  • warrantyactivatedAt, expiredAt, warrantyStatus và tham chiếu khách hàng.
  • repairTicketticketCode, repairStatus, totalAmount và thông tin tiếp nhận.
  • stockMovement có loại biến động, số lượng và tham chiếu chứng từ kho.

Khối entityData gồm:

  • Các trường lõi do đội kỹ thuật Qranty định nghĩa.
  • Các trường tùy chỉnh của doanh nghiệp trong customFields.
  • Các tham chiếu logic sang document khác.
  • Snapshot nhỏ cần giữ lại tại thời điểm phát sinh nghiệp vụ.
  • URL và metadata của hình ảnh hoặc file đính kèm.

Không lưu trực tiếp trong entityData:

  • File nhị phân lớn.
  • Hình ảnh dạng Base64.
  • Mảng dữ liệu tăng không giới hạn.
  • Password, OTP, token hoặc private key.
  • Các trường không có ý nghĩa nghiệp vụ rõ ràng.

5.3. Quy tắc phiên bản dữ liệu

Schemaless không có nghĩa là được phép thêm trường tùy ý.

Mỗi entityType phải có:

  • Hợp đồng dữ liệu.
  • Danh sách trường bắt buộc.
  • Danh sách trường tùy chọn.
  • Kiểu dữ liệu của từng trường.
  • Giới hạn kích thước.
  • Phiên bản entitySchemaVersion.
  • Validator tương ứng.
  • Reader có khả năng đọc document phiên bản cũ.

Khi bổ sung một trường mới:

  1. Xác định trường thuộc systemData hay entityData.
  2. Cập nhật hợp đồng dữ liệu.
  3. Tăng phiên bản cấu trúc nếu thay đổi có ảnh hưởng.
  4. Cập nhật validator.
  5. Cập nhật reader để vẫn đọc được document cũ.
  6. Chỉ backfill dữ liệu khi có nguồn đáng tin cậy.
  7. Không gán dữ liệu giả chỉ để document cũ giống document mới.

5.4. Lợi ích của cấu trúc hai khối

Lợi íchGiải thích
Cách ly tenant rõ ràngtenantId luôn nằm tại vị trí cố định
Index thống nhấtCác index hệ thống dùng cùng một đường dẫn
Định tuyến ổn địnhlevel, entityTypepartitionId có cấu trúc chung
Nghiệp vụ linh hoạtMỗi entityType có thể có các trường riêng
Nâng cấp độc lậpKhối hệ thống và khối nghiệp vụ có phiên bản riêng
Dễ kiểm thửKiểm tra systemData trước rồi kiểm tra entityData
Dễ tiếp quảnDev mới có thể nhận biết ngay dữ liệu hệ thống và dữ liệu nghiệp vụ

6. Phân loại 5 LV

Qranty V2 chia dữ liệu thành 5 LV dựa trên đặc tính vận hành, tốc độ tăng trưởng, cách đọc/ghi và thời gian lưu giữ.

Mỗi LV là một họ collection và có thể chứa nhiều entityType. LV không tương ứng trực tiếp với một màn hình hoặc một module giao diện.

LVNhóm dữ liệuĐặc tínhTốc độ tăngCách đọc/ghi chínhEntity tiêu biểu
LV1Cấu hìnhÍt dữ liệu, thay đổi ít nhưng ảnh hưởng rộngThấpĐọc nhiều, ghi íttenant, user, storeSettings, customFieldDefinition, integrationConfig, entitlement
LV2Danh mục và tham chiếuDữ liệu được tái sử dụng trong nhiều nghiệp vụThấp đến vừaĐọc nhiều, cập nhật có kiểm soátcatalog, supplier, sparePartCatalog, warrantyPolicy, messageTemplate
LV3Khách hàng và đối tácCó dữ liệu cá nhân và yêu cầu tìm kiếmVừaTìm theo tên, điện thoại, email và cập nhật hồ sơcustomer, businessPartner
LV4Giao dịch và trạng tháiYêu cầu tính đúng, liên kết nhiều nghiệp vụCaoGhi và cập nhật thường xuyênproductItem, warranty, repairTicket, stockMovement, debtEntry, payment
LV5Sự kiện và nhật kýChủ yếu ghi nối tiếp, tăng nhanh, có thời hạn lưuRất caoGhi nhiều, đọc theo thời gianauditEvent, domainEvent, notification, outboxMessage, idempotencyRecord, importJob

6.1. LV1 — Cấu hình

LV1 trả lời câu hỏi: hệ thống và doanh nghiệp đang được thiết lập như thế nào?

Dữ liệu thuộc LV1 gồm:

  • Thông tin tenant.
  • Tài khoản, vai trò và quyền.
  • Cấu hình cửa hàng.
  • Cấu hình cột hiển thị.
  • Định nghĩa trường tùy chỉnh.
  • Cấu hình tích hợp dịch vụ ngoài.
  • Gói sử dụng và quyền sử dụng module.
  • Định nghĩa loại dữ liệu và phiên bản hợp đồng dữ liệu.

Collection khởi tạo:

lv1_config_p0001

LV1 phù hợp với cache có thời hạn dài nhưng phải xóa cache ngay khi cấu hình thay đổi. Mọi thay đổi quan trọng phải được ghi audit tại LV5.

6.2. LV2 — Danh mục và tham chiếu

LV2 trả lời câu hỏi: doanh nghiệp đang kinh doanh hoặc sử dụng những danh mục nào?

Dữ liệu thuộc LV2 gồm:

  • Mẫu và loại sản phẩm.
  • Nhà cung cấp.
  • Danh mục linh kiện.
  • Chính sách bảo hành.
  • Mẫu tin nhắn.

Collection khởi tạo:

lv2_catalog_p0001

LV2 không lưu số lượng tồn kho hoặc lịch sử nhập xuất. Các biến động kho phải được lưu tại LV4.

6.3. LV3 — Khách hàng và đối tác

LV3 trả lời câu hỏi: ai đang mua hàng, sử dụng sản phẩm, bảo hành hoặc sửa chữa?

Dữ liệu thuộc LV3 gồm:

  • Tên khách hàng.
  • Số điện thoại và email.
  • Ngày sinh.
  • Địa chỉ.
  • Giới tính.
  • Phân loại khách hàng.
  • Nguồn tạo khách hàng.
  • Trường tùy chỉnh của tenant.

Collection khởi tạo:

lv3_customer_p0001

LV3 chứa dữ liệu cá nhân nên phải hạn chế log, cache và export. Khóa duy nhất như số điện thoại phải được kiểm soát xuyên partition nếu nghiệp vụ yêu cầu.

6.4. LV4 — Giao dịch và trạng thái

LV4 trả lời câu hỏi: nghiệp vụ nào đang diễn ra và trạng thái hiện tại là gì?

Dữ liệu thuộc LV4 gồm:

  • Lô sản phẩm và sản phẩm theo serial.
  • Bảo hành.
  • Phiếu sửa chữa.
  • Công việc và linh kiện sử dụng.
  • Phiếu nhập, xuất và điều chỉnh kho.
  • Dòng biến động tồn kho.
  • Công nợ và thanh toán.

Collection khởi tạo:

lv4_transaction_p0001

LV4 yêu cầu tính đúng cao nhất. Các thao tác phải chống ghi lặp, sử dụng recordVersion để tránh ghi đè và sinh audit event cho thay đổi quan trọng.

6.5. LV5 — Sự kiện, thông báo và nhật ký

LV5 trả lời câu hỏi: ai đã làm gì, vào thời điểm nào, hệ thống cần gửi thông tin gì và lỗi nào đã xảy ra?

Dữ liệu thuộc LV5 gồm:

  • Lịch sử thao tác.
  • Lịch sử thay đổi trạng thái.
  • Sự kiện nghiệp vụ.
  • Thông báo cho người dùng.
  • Outbox gửi sang hệ thống ngoài.
  • Khóa chống xử lý lặp.
  • Tiến trình import.
  • OTP có thời hạn.

Collection khởi tạo theo tháng:

lv5_event_202608_p0001

LV5 được chia theo tháng để thuận tiện cho lưu trữ, phục hồi và xóa dữ liệu theo thời hạn. Nếu một tháng vượt ngưỡng dung lượng thì mở partition tiếp theo trong cùng tháng.

6.6. Quan hệ logic giữa các nhóm dữ liệu

ERD logic nghiệp vụ Qranty V2

Hình 3 — Quan hệ nghiệp vụ giữa LV1–LV5 trong mô hình document. Đây là ERD logic, không phải khóa ngoại vật lý như cơ sở dữ liệu quan hệ.

Các document liên kết bằng ID logic và partitionId. Khi đọc document được tham chiếu, lớp truy cập dữ liệu vẫn phải kiểm tra tenantId; việc biết ID không đồng nghĩa với có quyền truy cập dữ liệu.

7. Quy tắc cách ly tenant

Qranty V2 sử dụng một database chung cho nhiều doanh nghiệp. Vì vậy, cách ly dữ liệu tenant là yêu cầu bắt buộc tại lớp xác thực, API, truy cập dữ liệu, index, cache và kiểm thử.

Mục tiêu là bảo đảm tenant A không thể đọc, cập nhật, xóa, thống kê hoặc export dữ liệu thuộc tenant B.

7.1. Xác định tenant

Mỗi doanh nghiệp có một tenantId nội bộ duy nhất và không thay đổi trong suốt vòng đời.

tenantId phải được lấy từ:

  • Phiên đăng nhập đã xác thực.
  • Access token hợp lệ.
  • Ngữ cảnh xác thực do backend tạo.
  • Tác vụ hệ thống có quyền và audit riêng.

Không sử dụng làm nguồn tenant đáng tin cậy:

  • tenantId do Web hoặc Mobile gửi trong request body.
  • tenantId trong query string chưa được xác thực.
  • Tên doanh nghiệp.
  • Email người dùng.
  • Subdomain.
  • Giá trị do client tự thay đổi.

Web và Mobile sử dụng chung API và dữ liệu nhưng không được truy cập trực tiếp MongoDB hoặc các collection system_*.

7.2. Quy tắc truy cập dữ liệu

Tất cả thao tác nghiệp vụ phải đi qua lớp truy cập dữ liệu dùng chung.

Lớp này có trách nhiệm:

  • Tự động thêm tenantId vào điều kiện truy vấn.
  • Tự động thêm tenantId khi tạo document mới.
  • Kiểm tra quyền của người dùng.
  • Kiểm tra recordStatus.
  • Kiểm tra recordVersion khi cập nhật.
  • Định tuyến tới đúng partition.
  • Ghi audit cho thao tác quan trọng.

Các quy tắc bắt buộc:

  • Mọi thao tác đọc phải có điều kiện tenant.
  • Mọi thao tác cập nhật phải có điều kiện tenant.
  • Mọi thao tác xóa phải có điều kiện tenant.
  • Mọi thao tác thống kê phải giới hạn theo tenant.
  • Mọi thao tác import và export phải kiểm tra tenant.
  • ID của document không đủ để cấp quyền truy cập.
  • Dev không được bỏ qua lớp kiểm soát tenant để truy vấn collection trực tiếp.
  • Tenant A cung cấp ID của tenant B phải nhận kết quả không tìm thấy.
  • API không được để lộ rằng document của tenant khác đang tồn tại.

Tài khoản quản trị toàn hệ thống phải sử dụng:

  • API quản trị riêng.
  • Quyền riêng.
  • Phạm vi truy cập rõ ràng.
  • Audit riêng.
  • Cơ chế phê duyệt đối với thao tác nhạy cảm.

7.3. Quy tắc index theo tenant

Các index phục vụ truy vấn trong phạm vi doanh nghiệp phải bắt đầu bằng systemData.tenantId.

Ví dụ, index tìm khách hàng theo số điện thoại gồm:

  • systemData.tenantId.
  • systemData.entityType.
  • entityData.customerPhone.

Index tìm phiếu sửa theo trạng thái gồm:

  • systemData.tenantId.
  • systemData.entityType.
  • entityData.repairStatus.
  • systemData.createdAt.

Không tạo index cho toàn bộ trường trong entityData.

Một index mới chỉ được tạo khi:

  • Có luồng truy vấn thực tế.
  • Có số liệu hoặc kế hoạch đo hiệu năng.
  • Đã kiểm tra query plan.
  • Đã đánh giá chi phí dung lượng.
  • Đã đánh giá ảnh hưởng tới tốc độ ghi.

7.4. Quy tắc cache theo tenant

Mọi cache key phải chứa tenantId.

Cache key cần xác định tối thiểu:

  • Tenant sở hữu dữ liệu.
  • entityType.
  • ID hoặc điều kiện truy vấn.
  • Phiên bản dữ liệu nếu cần.
  • Phạm vi quyền nếu kết quả phụ thuộc quyền người dùng.

Không được sử dụng chung một cache key cho nhiều tenant.

Cache không phải nguồn dữ liệu chính cho:

  • Tồn kho.
  • Công nợ.
  • Trạng thái bảo hành.
  • Trạng thái sửa chữa.
  • Thanh toán.
  • Quyền truy cập.

Khi dữ liệu được cập nhật, hệ thống phải xóa hoặc cập nhật cache tương ứng của đúng tenant.

7.5. Quy tắc tham chiếu giữa các document

Mỗi tham chiếu quan trọng cần xác định:

  • ID của document đích.
  • entityType của document đích.
  • LV của document đích.
  • partitionId của document đích.

Khi đọc document được tham chiếu, hệ thống vẫn phải kiểm tra tenantId. Việc document nguồn chứa ID của document đích không đồng nghĩa với việc người dùng được quyền truy cập document đích.

Không lưu tên collection vật lý trong tham chiếu nghiệp vụ.

7.6. Kiểm thử cách ly bắt buộc

Mỗi API nghiệp vụ phải có kiểm thử chéo tenant theo trình tự:

  1. Tenant A tạo một document.
  2. Tenant A đọc được document vừa tạo.
  3. Tenant B sử dụng ID của document thuộc tenant A để đọc.
  4. API trả về kết quả không tìm thấy.
  5. Nội dung document không xuất hiện trong response hoặc log.
  6. Tenant B thử cập nhật document của tenant A.
  7. Không có trường nào trong document bị thay đổi.
  8. Tenant B thử xóa document của tenant A.
  9. Document không bị xóa hoặc đổi trạng thái.
  10. Tenant A vẫn đọc được document ban đầu.
  11. Log bảo mật ghi nhận truy cập bất thường nhưng không làm lộ dữ liệu.

Kiểm thử cách ly phải được áp dụng cho:

  • Đọc một document.
  • Đọc danh sách.
  • Tìm kiếm.
  • Phân trang.
  • Cập nhật.
  • Xóa mềm.
  • Import.
  • Export.
  • Thống kê.
  • Tham chiếu chéo.
  • Cache.
  • Tác vụ nền.
  • Thông báo.
  • File đính kèm.

7.7. Bảo vệ dữ liệu cá nhân

LV3 và các snapshot khách hàng trong LV4 phải tuân thủ các quy tắc:

  • Không ghi password, OTP hoặc token vào log.
  • Che một phần số điện thoại và email trong log.
  • Không log toàn bộ hồ sơ khách hàng.
  • Giới hạn người được phép export.
  • Ghi audit cho mỗi lần export.
  • Không sử dụng dữ liệu Production thật trong Development và Test.
  • Mã hóa dữ liệu khi truyền qua mạng.
  • Mã hóa bản sao lưu.
  • Không nhân bản dữ liệu cá nhân nếu không có yêu cầu nghiệp vụ.
  • Có quy trình chỉnh sửa hoặc xóa dữ liệu theo chính sách doanh nghiệp.

7.8. Điều kiện cách ly tenant đạt yêu cầu

Cơ chế cách ly tenant được xem là đạt khi:

  • Không có truy vấn nghiệp vụ bỏ qua tenantId.
  • Tenant A không đọc được dữ liệu tenant B.
  • Tenant A không cập nhật hoặc xóa được dữ liệu tenant B.
  • Cache không trả dữ liệu chéo tenant.
  • Thống kê và export không chứa dữ liệu tenant khác.
  • Log không làm lộ dữ liệu cá nhân.
  • Kiểm thử tự động bao phủ các luồng truy cập chính.
  • Mọi thao tác quản trị đặc biệt đều có quyền và audit rõ ràng.

8. Quy tắc mở rộng và partition

Partition được sử dụng để giới hạn kích thước vận hành của một collection vật lý mà không thay đổi ý nghĩa nghiệp vụ của LV.

Ngưỡng dung lượng được tính bằng tổng dung lượng dữ liệu và index. Đây là ngân sách vận hành của Qranty, không phải giới hạn kỹ thuật cố định của MongoDB.

MứcDung lượngHành động
Bình thườngDưới 7 GiBTiếp tục nhận document mới
Cảnh báoTừ 7 GiBKiểm tra tốc độ tăng, document lớn và index
Chuẩn bị chuyểnTừ 8 GiBTạo partition mới và chuyển luồng ghi mới
Nguy hiểmTừ 9 GiBĐiều tra tăng trưởng bất thường và xử lý ưu tiên cao
Ngân sách tối đa10 GiBKhông chủ động ghi document mới vào partition này

Hệ thống chuyển luồng ghi từ 8 GiB thay vì chờ đủ 10 GiB vì:

  • Document cũ vẫn có thể tiếp tục được cập nhật và tăng kích thước.
  • Index vẫn tăng khi document cũ thay đổi.
  • Tạo collection, validator và index mới cần thời gian.
  • Cần giữ khoảng trống để tránh phải dừng ghi đột ngột.

Quy trình mở partition theo dung lượng

Hình 4 — Partition cũ cảnh báo từ 7 GiB, mở partition kế tiếp từ 8 GiB và giữ phần dung lượng còn lại cho cập nhật document cũ cùng index.

8.1. Registry partition

system_partitions là registry định tuyến giữa partitionId logic và collection vật lý.

Registry lưu các thông tin:

TrườngÝ nghĩa
levelLV1 đến LV5
familyHọ dữ liệu config, catalog, customer, transaction hoặc event
partitionIdĐịnh danh partition logic
collectionNameCollection vật lý
statepreparing, active, closed hoặc archived
acceptNewDocumentsCó nhận document mới hay không
periodTháng áp dụng đối với LV5
dataBytesDung lượng dữ liệu gần nhất
indexBytesDung lượng index gần nhất
documentCountSố document gần nhất
warningAtBytesMốc cảnh báo
rotateAtBytesMốc chuyển partition
budgetBytesNgân sách vận hành
schemaTemplateVersionPhiên bản validator
indexTemplateVersionPhiên bản bộ index
lastMeasuredAtThời điểm đo gần nhất

API không được tự đoán tên collection từ level hoặc entityType. API phải lấy ánh xạ từ registry và cache ánh xạ trong thời gian ngắn.

8.2. Quy trình mở partition

Ví dụ khi lv3_customer_p0001 đạt ngưỡng chuyển:

  1. Tiến trình quản lý dung lượng lấy khóa nguyên tử.
  2. Tạo collection lv3_customer_p0002.
  3. Áp dụng validator đúng phiên bản.
  4. Tạo đầy đủ index bắt buộc.
  5. Tạo bản ghi lv3-0002 trong system_partitions.
  6. Đánh dấu lv3-0002 là partition nhận document mới.
  7. Đóng nhận document mới đối với lv3-0001.
  8. Xác nhận tại mọi thời điểm chỉ có một partition nhận dữ liệu mới.
  9. Giải phóng khóa sau khi hoàn tất.

Sau khi chuyển:

  • Document mới được ghi vào p0002.
  • Document cũ tại p0001 vẫn đọc và cập nhật được.
  • Không tự động chuyển toàn bộ document cũ sang partition mới.
  • Tham chiếu cũ vẫn hoạt động thông qua partitionId.
  • Khoảng dung lượng còn lại của p0001 dành cho cập nhật và index.

8.3. Theo dõi dung lượng

Không gọi thống kê dung lượng trong từng request vì thao tác này làm tăng độ trễ.

Tiến trình nền thực hiện:

  • Đo LV1 đến LV3 theo chu kỳ 30 phút.
  • Đo LV4 và LV5 theo chu kỳ 5 phút.
  • Ghi dung lượng mới nhất vào system_partitions.
  • Tính tốc độ tăng theo giờ và ngày.
  • Phát cảnh báo khi vượt ngưỡng.
  • Chỉ mở một partition bằng khóa nguyên tử.

8.4. Quy tắc riêng cho LV5

LV5 được chia trước theo tháng:

  • Đầu tháng tạo collection lv5_event_YYYYMM_p0001.
  • Hết tháng, collection cũ ngừng nhận sự kiện mới.
  • Nếu trong tháng đạt 8 GiB thì tạo p0002 trong cùng tháng.
  • Sự kiện đến muộn được ghi đúng tháng trong thời gian cho phép.
  • Sau thời gian cho phép, partition chuyển sang chỉ đọc.
  • Dữ liệu hết thời hạn được xóa hoặc archive theo chính sách của từng loại sự kiện.

8.5. Khóa duy nhất xuyên partition

MongoDB unique index chỉ đảm bảo tính duy nhất trong một collection vật lý. Vì vậy, dữ liệu cần duy nhất xuyên partition phải được giữ chỗ trong system_unique_keys.

Các khóa cần kiểm soát gồm:

  • tenantCode.
  • Username hoặc email đăng nhập.
  • SKU trong phạm vi tenant.
  • Serial sản phẩm trong phạm vi tenant.
  • Mã phiếu sửa trong phạm vi tenant.
  • Mã bảo hành hoặc QR token.
  • Số điện thoại đã chuẩn hóa nếu nghiệp vụ yêu cầu duy nhất.

Quy trình giữ khóa:

  1. Chuẩn hóa giá trị.
  2. Tạo bản ghi giữ chỗ theo phạm vi hệ thống hoặc tenant.
  3. Nếu khóa đã tồn tại thì từ chối hoặc chuyển sang luồng gộp dữ liệu.
  4. Ghi document nghiệp vụ.
  5. Liên kết bản giữ chỗ tới document và partitionId.
  6. Hoàn tác giữ chỗ nếu thao tác nghiệp vụ thất bại.

Xóa mềm document không đồng nghĩa với việc tự động giải phóng khóa duy nhất.

9. Phương án migration

Không sửa trực tiếp database cũ thành Qranty V2. Hệ thống tạo database V2 mới và chuyển dữ liệu theo từng giai đoạn có checkpoint, đối soát và khả năng quay lại.

Giai đoạn 1 — Chốt ánh xạ

  • Liệt kê toàn bộ model và collection cũ.
  • Ánh xạ từng model vào LV và entityType.
  • Xác định ID được giữ nguyên.
  • Xác định trường cần đổi tên hoặc chuẩn hóa.
  • Xác định dữ liệu trùng.
  • Xác định document thiếu tenantId.
  • Xác định dữ liệu không còn được sử dụng.
  • Chốt quy tắc chuyển đổi trước khi chạy migration.

Kết quả của giai đoạn này là bảng ánh xạ được review và phê duyệt.

Giai đoạn 2 — Tạo nền Qranty V2

  • Tạo database qranty_v2.
  • Tạo ba collection kỹ thuật.
  • Tạo năm collection nghiệp vụ đầu tiên.
  • Áp dụng validator.
  • Tạo index bắt buộc.
  • Tạo hợp đồng dữ liệu phiên bản 1.
  • Bật metric và cảnh báo.
  • Kiểm tra quyền truy cập database.

Kết quả của giai đoạn này là nền V2 trống nhưng sẵn sàng nhận dữ liệu.

Giai đoạn 3 — Chuyển dữ liệu nền

Dữ liệu được chuyển theo từng tenant và theo thứ tự:

LV1 → LV2 → LV3 → LV4 → LV5

Mỗi document phải:

  • Được chuẩn hóa điện thoại, email, mã và trạng thái.
  • Được gắn tenantId.
  • Được tạo systemData.
  • Được xác định entityType.
  • Được gắn partitionId.
  • Được kiểm tra validator.
  • Được ghi checkpoint.
  • Được lưu ánh xạ ID nếu không giữ được ID cũ.

system_migration_jobs lưu tiến độ theo tenant và entityType. Nếu tiến trình bị dừng, lần chạy sau tiếp tục từ checkpoint thay vì chuyển lại toàn bộ.

Giai đoạn 4 — Đồng bộ thay đổi

Trong quá trình chuyển nền, hệ thống cũ vẫn là nguồn dữ liệu chính.

Tiến trình đồng bộ phải:

  • Ghi thay đổi mới từ hệ thống cũ sang V2.
  • Chống thực hiện lặp.
  • Theo dõi độ trễ đồng bộ.
  • Ghi lại lỗi theo tenant.
  • Không để lỗi của một tenant dừng toàn bộ tiến trình.
  • Cho phép chạy lại tác vụ bị lỗi từ checkpoint.

Giai đoạn 5 — Đối soát

Dữ liệu phải được đối soát theo tenant và entityType.

Các chỉ số bắt buộc:

  • Tổng số document.
  • Tổng số document theo trạng thái.
  • Tổng tồn kho.
  • Tổng công nợ.
  • Số bảo hành còn hiệu lực.
  • Số phiếu sửa theo trạng thái.
  • Khóa duy nhất.
  • Document mồ côi.
  • Tham chiếu sai partition.
  • Document vượt giới hạn kích thước.
  • Document thiếu trường hệ thống bắt buộc.

Tenant chỉ được chuyển sang bước tiếp theo khi các sai lệch quan trọng đã được xử lý.

Giai đoạn 6 — Đọc bóng

Web và Mobile vẫn trả dữ liệu từ hệ thống cũ cho người dùng. API đồng thời đọc dữ liệu V2 ở nền để so sánh.

Quy trình đọc bóng:

  1. Thực hiện truy vấn trên hệ thống cũ.
  2. Thực hiện truy vấn tương ứng trên V2.
  3. Chuẩn hóa kết quả so sánh.
  4. Ghi lại chênh lệch.
  5. Phân loại nguyên nhân.
  6. Sửa ánh xạ hoặc dữ liệu.
  7. Chạy lại tenant bị lỗi.

Đọc bóng không được làm thay đổi kết quả người dùng đang nhận trong giai đoạn kiểm chứng.

Giai đoạn 7 — Chuyển từng tenant

  • Chọn tenant thử nghiệm có dữ liệu đại diện.
  • Đồng bộ phần dữ liệu còn lại.
  • Tạm dừng ghi trong thời gian ngắn nếu cần.
  • Chuyển API của tenant thử nghiệm sang V2.
  • Theo dõi lỗi, độ trễ, tồn kho, công nợ và bảo hành.
  • Xác nhận Web và Mobile cùng sử dụng nguồn dữ liệu V2.
  • Mở rộng sang nhóm tenant tiếp theo khi tenant thử nghiệm đạt tiêu chí.

Không chuyển toàn bộ tenant trong một lần phát hành.

Giai đoạn 8 — Kế hoạch quay lại

Trong thời gian ổn định:

  • Giữ database cũ ở trạng thái có thể đọc.
  • Giữ nhật ký thay đổi cần thiết để đối soát.
  • Duy trì khả năng chuyển tenant về đường đọc/ghi cũ.
  • Xác định rõ điều kiện kích hoạt rollback.
  • Không xóa database cũ trước khi hết thời gian theo dõi.
  • Chỉ kết thúc migration sau khi có phê duyệt của Kỹ sư trưởng.

Rollback phải thực hiện theo tenant, không bắt buộc quay lại toàn bộ hệ thống nếu lỗi chỉ ảnh hưởng một tenant.

10. Hệ quả

10.1. Hệ quả tích cực

  • Số lượng database không tăng tương ứng với số doanh nghiệp.
  • Một bộ validator, index và quy trình migration được áp dụng thống nhất.
  • Web và Mobile sử dụng chung API, collection và dữ liệu nghiệp vụ.
  • Dữ liệu được cách ly bằng tenantId tại vị trí cố định.
  • Trường nghiệp vụ mới có thể được bổ sung thông qua phiên bản dữ liệu.
  • Document cũ thiếu trường mới vẫn có thể được đọc bằng reader tương thích.
  • Dung lượng được mở rộng bằng partition mà không thay đổi ý nghĩa LV.
  • Có thể theo dõi dung lượng, số document và tốc độ tăng theo từng họ dữ liệu.
  • Các khóa duy nhất được bảo vệ xuyên partition.
  • Việc chuyển dữ liệu có checkpoint, đối soát và phương án rollback.
  • Tài liệu ADR tạo cơ sở để dev mới tiếp quản và mở rộng Qranty V2.

10.2. Rủi ro, ảnh hưởng và biện pháp kiểm soát

Rủi roẢnh hưởngBiện pháp kiểm soát
Dev quên lọc tenantIdTruy cập chéo dữ liệu doanh nghiệpLớp truy cập dữ liệu dùng chung, kiểm thử chéo tenant và review truy vấn
Cache không chứa tenantIdTrả dữ liệu của tenant khácChuẩn hóa cache key và kiểm thử cache giữa hai tenant
Unique index chỉ có hiệu lực trong một partitionTrùng serial, SKU hoặc số điện thoạiSử dụng system_unique_keys
Hai partition cùng nhận document mớiDữ liệu phân tán không kiểm soátChuyển partition bằng khóa nguyên tử
Quá nhiều partitionTruy vấn danh sách và tìm kiếm chậmRegistry định tuyến, giới hạn fan-out và theo dõi hiệu năng
Document chứa mảng tăng vô hạnDocument quá lớn và cập nhật chậmGiới hạn mảng, tách document con và kiểm tra kích thước
Index quá nhiềuTăng dung lượng và làm chậm thao tác ghiChỉ tạo index từ truy vấn thực tế và kiểm tra query plan
Dữ liệu cũ thiếu trường mớiCode mới không đọc được document cũReader tương thích, giá trị mặc định và backfill có chọn lọc
Migration làm sai tồn kho hoặc công nợSai dữ liệu nghiệp vụ quan trọngĐối soát, đọc bóng, chuyển từng tenant và rollback
LV5 tăng quá nhanhTăng chi phí lưu trữ và phục hồiChia theo tháng, TTL, archive và giới hạn payload
Dữ liệu cá nhân xuất hiện trong logRủi ro bảo mật và quyền riêng tưMask dữ liệu, giới hạn log và audit thao tác export
Registry partition sai ánh xạAPI đọc hoặc ghi sai collectionSao lưu đồng bộ, kiểm tra nhất quán và cache có TTL ngắn

10.3. Chi phí của quyết định

Thiết kế này yêu cầu đội kỹ thuật xây dựng và duy trì:

  • Lớp truy cập dữ liệu bắt buộc tenant.
  • Registry quản lý partition.
  • Cơ chế giữ khóa duy nhất xuyên partition.
  • Validator và hợp đồng dữ liệu theo phiên bản.
  • Tiến trình đo dung lượng và mở partition.
  • Hệ thống metric và cảnh báo.
  • Công cụ migration, đối soát và rollback.
  • Bộ kiểm thử cách ly tenant.
  • Hướng dẫn cho dev khi thêm entityType hoặc trường mới.

Chi phí triển khai ban đầu cao hơn mô hình collection đơn giản, nhưng giảm rủi ro và chi phí vận hành khi số tenant và dữ liệu tăng.

Hệ quả tích cực

  • Không tăng database theo số doanh nghiệp.
  • Web và Mobile sử dụng chung dữ liệu.
  • Dữ liệu cũ vẫn có thể đọc khi thêm trường mới.
  • Có thể mở rộng dung lượng theo partition.

Rủi ro và hạn chế

  • Dev có thể quên lọc tenantId.
  • Unique index xuyên partition cần cơ chế giữ chỗ riêng.
  • Truy vấn nhiều partition có thể làm tăng độ trễ.
  • Migration cần đối soát và rollback cẩn thận.

V. Tài liệu tham khảo

Theo yêu cầu trình bày, phần này không chèn liên kết trực tiếp. Người đọc có thể sao chép nguyên tên tài liệu và tên nguồn bên dưới để tìm kiếm trên Google.

1. MongoDB Documentation

  • Data Modeling in MongoDB — MongoDB Database Manual.
  • Schema Validation — MongoDB Database Manual.
  • Specify Validation Level for Existing Documents — MongoDB Database Manual.
  • Compound Indexes — MongoDB Database Manual.
  • TTL Indexes — MongoDB Database Manual.
  • MongoDB Limits and Thresholds — MongoDB Database Manual.
  • Transactions — MongoDB Database Manual.
  • Change Streams — MongoDB Database Manual.
  • Sharding — MongoDB Database Manual.

2. Kiến trúc Multi-Tenant và bảo mật

  • Tenancy Models for a Multitenant Solution — Microsoft Azure Architecture Center.
  • Architectural Approaches for Storage and Data in Multitenant Solutions — Microsoft Azure Architecture Center.
  • Multi-Tenant Application Security Cheat Sheet — OWASP Cheat Sheet Series.

Các tài liệu trên được dùng để đối chiếu các quyết định về mô hình chia sẻ dữ liệu, cách ly tenant, flexible schema có validator, index, TTL, transaction, change stream, giới hạn MongoDB và thời điểm cân nhắc sharding. Các ngưỡng 7/8/9/10 GiB và cách phân loại LV1–LV5 là quy ước vận hành nội bộ do Qranty đề xuất, không phải giới hạn mặc định của MongoDB.

VI. Next Steps of Documentation

  • 07/2026: Hoàn thành bản đề xuất kiến trúc đầu tiên.
  • 08/2026: Tinh lọc ADR và tạo Merge Request vào repo g1.
  • 08/2026: Chờ Kỹ sư trưởng và đội kỹ thuật review.
  • mm/yyyy: Chuyển trạng thái ADR từ Proposed sang Accepted hoặc Rejected.
  • mm/yyyy: Xuất bản phiên bản đã tinh lọc lên docs.qranty.com.