ADR-0003. Trường phân biệt type của product variant + quan hệ ProductBundler
| Trường | Giá trị |
|---|---|
| Status | Accepted |
| Ngày | 2026-04-10 |
| Người quyết định | Phat Nguyen |
| Thay thế | - |
Bối cảnh
- Catalog phải biểu diễn nhiều hành vi khác nhau: hàng vật lý có tồn kho, hàng tiêu hao không lưu kho, dịch vụ, kit/hàng sản xuất (định mức nguyên vật liệu), combo ảo, addon miễn phí, và gợi ý "thường được mua cùng nhau".
- Một mô hình trước đây đã nạp chồng
Category.type(COMBO/ADDON/FBT) để điều khiển hành vi tồn kho và bán hàng. Cách này dễ vỡ: phân loại lại một sản phẩm có thể lật nó qua lại giữa vật lý và ảo, trong khi category về bản chất chỉ là chuyện gom nhóm ở phía FE. - Inventory và sale cần một tín hiệu cấu trúc duy nhất, ổn định để trả lời "cái này có lưu kho không?" và "khi bán nó có tách thành các thành phần không?".
Quyết định
Tách vấn đề này thành một trường phân biệt cấu trúc theo từng variant và một bảng quan hệ riêng:
ProductVariant.type(ProductVariantTypes,core/.../public/product-variant/constants.ts) là trường phân biệt cấu trúc duy nhất:STORABLE(mặc định) /MANUFACTURED→ có lưu kho (STOCKABLE_SET).CONSUMABLE/SERVICE→ không lưu kho.KIT/COMBO/MANUFACTURED→ có BOM (BOMABLE_SET).COMBOlà ảo - không cóInventoryItem; khi thêm vào giỏ nó tách thành các variant thành phần.
ProductBundlerlà bảng quan hệ duy nhất cho COMBO / ADDON / FBT (ProductBundlerTypes), khóa theo(type, leadVariantId, relatedVariantId). ADDON và FBT là quan hệ, không phải type của variant - một addon variant là một PV vật lý thông thường; chính hàng quan hệ mới biến nó thành addon.Category.typetrở thành nhãn gom nhóm FE thuần tuý - không có gì trong inventory/sale rẽ nhánh dựa trên nó nữa.
Việc tách combo khi thêm vào giỏ được triển khai trong @nx/sale / @nx/inventory - xem ADR liên package.
Hệ quả
| Ưu | Nhược |
|---|---|
| Tín hiệu cấu trúc ổn định, độc lập với việc phân loại | Hai khái niệm (type của variant và type của bundler) - không được gộp lẫn |
| Phân loại lại một sản phẩm là an toàn (không thể lật vật lý↔ảo) | Cần migration để backfill type từ dữ liệu cũ dựa trên category |
Một bảng quan hệ bao quát gọn COMBO/ADDON/FBT với basis giá tuỳ chọn | Tính có hướng của FBT đòi hỏi một hàng cho mỗi chiều |
Inventory chỉ khởi tạo item cho các variant thuộc STOCKABLE_SET | Consumer phải đọc các helper ProductVariantTypes, không phải category |
Phương án đã cân nhắc
| Tùy chọn | Ưu | Nhược | Vì sao bị từ chối |
|---|---|---|---|
Giữ Category.type làm trường phân biệt | Không phải đổi schema | Dễ vỡ; phân loại lại làm lật hành vi; trộn lẫn việc gom nhóm với cấu trúc | Đây chính là nguyên nhân gốc của lỗi đang được sửa |
| Mỗi quan hệ một bảng riêng (combo/addon/FBT) | Tường minh | Ba bảng gần như giống hệt nhau; logic trùng lặp | Một bảng ProductBundler với cột type đơn giản hơn |
Dùng cờ boolean trên ProductVariant (isCombo, isKit…) | Đơn giản | Các cờ loại trừ lẫn nhau mô hình hoá một enum hữu hạn rất kém | Một enum type có kiểu duy nhất mới đúng theo code style |
Tham khảo
packages/core/src/models/schemas/public/product-variant/constants.ts(ProductVariantTypes,STOCKABLE_SET,BOMABLE_SET)packages/core/src/models/schemas/public/product-bundler/{schema,constants}.ts- Liên package:
developer/packages/inventory/decisions/0006-combo-explosion-at-cart-add