Domain Model
@nx/searchkhông sở hữu bảng PostgreSQL nào. "Domain" của nó là tập các Typesense collection - projection đọc phi chuẩn hoá của các bảng nguồn trong schemapublic,pricing, vàinventory. Định nghĩa schema nằm trongsrc/configurations/<entity>.collection.ts(cấu trúc dùng chung trongschema-fragments.ts).
1. ERD đầy đủ
Bảng nguồn CDC (trái) được chiếu vào Typesense collection (phải). Nét liền = nguồn document trực tiếp; nét đứt = chỉ cascade.
2. Entities (Collections)
Mỗi khối là một collection. Mỗi document Typesense có cấu trúc khá phẳng; i18n
{ en, vi }được làm phẳng thànhfield.en/field.vi.queryByliệt kê các trường text có thể tìm; các trường facetable có thể lọc.
organizers
| Thuộc tính | Giá trị |
|---|---|
| Source | public.Organizer (trực tiếp) |
| Config | src/configurations/organizer.collection.ts |
| Doc id | Organizer.id |
| queryBy | name.en, name.vi, description.en, description.vi, slug, identifier |
| Relations | parent (organizers, bù dữ liệu) |
| Embedding | tuỳ chọn |
merchants
| Thuộc tính | Giá trị |
|---|---|
| Source | public.Merchant (trực tiếp) |
| Config | src/configurations/merchant.collection.ts |
| Doc id | Merchant.id |
| queryBy | name.en, name.vi, description.en, description.vi, slug, identifier |
| Relations | organizer (join gốc), parent (merchants, bù dữ liệu) |
categories
| Thuộc tính | Giá trị |
|---|---|
| Source | public.Category (trực tiếp) |
| Config | src/configurations/category.collection.ts |
| Doc id | Category.id |
| queryBy | name.en, name.vi, description.en, description.vi |
| Relations | merchant (join gốc) |
devices
| Thuộc tính | Giá trị |
|---|---|
| Source | public.Device (trực tiếp) |
| Config | src/configurations/device.collection.ts |
| Doc id | Device.id |
| queryBy | name.*, identifier, code |
| Relations | organizer (join gốc), merchant (join gốc) |
sale-channels
| Thuộc tính | Giá trị |
|---|---|
| Source | public.SaleChannel (trực tiếp) |
| Config | src/configurations/sale-channel.collection.ts |
| Doc id | SaleChannel.id |
| queryBy | name.en, name.vi, slug, identifier |
| Relations | merchant (join gốc), parent (sale-channels, bù dữ liệu) |
products
| Thuộc tính | Giá trị |
|---|---|
| Source | public.Product + public.ProductInfo (i18n) - cả hai trực tiếp (info là cập nhật một phần) |
| Cascade | ProductCategory → categoryIds[] |
| Config | src/configurations/product.collection.ts (+ product-info-mapper) |
| Doc id | Product.id |
| queryBy | identifier, slug, info.name.{en,vi}, info.description.{en,vi}, merchant_name.{en,vi}, organizer_name.{en,vi}, sale_channel_names |
| Facets | status, merchantId (ref merchants.id), categoryIds[], parentId, tên parent đã phi chuẩn hoá |
| Relations | merchant (join gốc), parent (products, bù dữ liệu) |
| Embedding | trường embedding từ [identifier, slug] |
product-variants
| Thuộc tính | Giá trị |
|---|---|
| Source | public.ProductVariant (trực tiếp) + ProductVariantInfo |
| Cascade | ProductCategory (categoryIds), MetaLink (metaLinks), FareSet/Fare (fareSet, defaultPrice), ProductBundler (comboItems) |
| Config | src/configurations/product-variant.collection.ts |
| Doc id | ProductVariant.id |
| Relations | product (join gốc), merchant (join gốc) |
inventories
| Thuộc tính | Giá trị |
|---|---|
| Source | inventory.InventoryStock (trực tiếp, mỗi document một dòng) |
| Cascade | InventoryItem, InventoryLocation, InventoryIdentifier, Material |
| Config | src/configurations/inventory.collection.ts |
| Doc id | InventoryStock.id (== inventoryStockId) |
| queryBy | item.name.{en,vi}, location.name.{en,vi}, identifiers.value, stock.lotNumber, stock.serialNumber, item.slug, item.description.{en,vi} |
| Structure | nhóm lồng item / location / stock; identifiers[] (không phụ thuộc scheme); flags[] (boolean dẫn xuất) |
| Notes | flags.isExpired / isExpiringSoon có thể cũ - được làm mới bằng reindex định kỳ hoặc khoảng giá trị tại thời điểm truy vấn |
3. Bất biến xuyên entity
| Bất biến | Cách thực thi |
|---|---|
Document id luôn bằng id dòng nguồn | Mapper extractDocumentId / idField |
| Dòng nguồn bị soft-delete ⇒ document không tồn tại | Mapper trả về null → CDCService xoá |
| Event CDC sai thứ tự không bao giờ ghi đè trạng thái mới hơn | Bộ chặn LSN (source_lsn) + so sánh deleted_at |
products.categoryIds phản ánh mọi dòng ProductCategory | Lan toả cascade + bổ sung dữ liệu (không phải mapper của dòng) |
Document inventories là một-trên-mỗi-InventoryStock, item/location đã phi chuẩn hoá | Cascade từ InventoryItem/InventoryLocation |
Trường reference của Typesense phải ở cấp cao nhất | Schema đặt merchantId, inventoryItemId, v.v. ở cấp cao nhất |
4. Hành vi Soft-delete
| Hành vi | Chi tiết |
|---|---|
| Nguồn dữ liệu chính | PostgreSQL deletedAt (do service nguồn sở hữu) |
| Ảnh hưởng tới tìm kiếm | Mapper phát hiện deletedAt != null → phát lệnh xoá tới Typesense |
| Đọc mặc định | Dòng bị soft-delete không có document, nên không bao giờ xuất hiện trong kết quả |
| Xoá cứng | Một op Debezium d cũng xoá document |
| Khôi phục | Một event CDC không-bị-xoá sau đó sẽ upsert lại document |