ADR-0005. Lưu trữ UoM — uom jsonb trên catalog + uomId soft ref trên line
| Trường | Giá trị |
|---|---|
| Status | Accepted |
| Date | 2026-04-05 |
| Deciders | inventory-team, PM |
| Supersedes | — |
Bối cảnh
Materialcó nhiều "vai trò" UoM: base (đơn vị lưu kho, vd gram), purchase (đơn vị hóa đơn vendor, vd kg), sale (đơn vị hiển thị, vd portion).PurchaseOrderItem,VendorItem,MaterialRecipeItem,InventoryTrackingmỗi cái có một context UoM đơn lẻ trên mỗi row.- FK chặt từ bảng line tới
UnitOfMeasuremong manh: row UoM là reference data và có thể bị soft-delete hoặc đổi key bởi merchant.
Quyết định
Lưu trữ hai lớp:
- Catalog entity (
Material,ProductVariant) lưu các vai trò trong một cột jsonbuomcó dạng:tstype TUomRoles = { base: { id: string; code: string; name: I18n; ratio: number }; purchase?: { ... }; sale?: { ... }; }; - Line entity (
PurchaseOrderItem,VendorItem,MaterialRecipeItem,InventoryTracking) lưuuomId: textnhư một soft reference cộng vớimultiplier: decimal(15,4). Khai báorelations()trongmodel.tscung cấp accessoruomcho query; DB không có foreign key.
Hệ quả
| Pros | Cons |
|---|---|
Snapshot catalog ổn định — kể cả khi UoM của merchant bị xóa, Material.uom vẫn còn | Cần validate ở service-layer rằng uomId resolve được lúc ghi |
Line entity tách rời khỏi vòng đời UnitOfMeasure | Không có tính toàn vẹn tham chiếu cấp DB cho uomId |
multiplier được capture lúc ghi line → quy đổi đơn vị bất biến sau đó | Hai dạng lưu trữ phải nhớ (jsonb vs uomId) |
Replay/audit hoạt động không cần join UnitOfMeasure | Báo cáo phải cẩn thận dùng multiplier cấp line, không phải uom cấp catalog |
Phương án thay thế đã cân nhắc
| Phương án | Pros | Cons | Lý do từ chối |
|---|---|---|---|
FK chặt uomId → UnitOfMeasure.id ở mọi nơi | Toàn vẹn tham chiếu | Vỡ khi soft-delete UoM; mong manh khi merchant override | Sai primitive cho reference data |
| Một jsonb trên mỗi line entity | Không lo FK | Lặp dữ liệu UoM trên mỗi line; khó query "tất cả line đơn vị kg" | Lãng phí storage, đau đầu query |
UnitOfMeasure với row có version (không soft-delete) | ID ổn định | Thay đổi thiết kế lớn, không giải quyết nhu cầu snapshot multiplier | Ngoài phạm vi |
Tham chiếu
core/src/models/schemas/inventory/material/schema.ts(uomjsonb)core/src/models/schemas/inventory/purchase-order-item/schema.ts(uomIdsoft ref +model.tsuomrelation)- Memory:
feedback_uom_storage_convention.md