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 denormalized của các bảng nguồn trong schemapublic,pricing, vàinventory. Định nghĩa schema nằm trongsrc/configurations/<entity>.collection.ts(shape dùng chung trongschema-fragments.ts).
1. ERD đầy đủ
Bảng source CDC (trái) project vào Typesense collection (phải). Liền = direct doc source; nét đứt = cascade-only.
2. Entities (Collections)
Mỗi block một collection. Mỗi cái là một Typesense document khá phẳng; i18n
{ en, vi }được flatten thànhfield.en/field.vi.queryByliệt kê các trường text có thể tìm; trường facetable có thể filter.
organizers
| Thuộc tính | Giá trị |
|---|---|
| Source | public.Organizer (direct) |
| Config | src/configurations/organizer.collection.ts |
| Doc id | Organizer.id |
| queryBy | name.en, name.vi, description.en, description.vi, slug, identifier |
| Relations | parent (organizers, hydration) |
| Embedding | optional |
merchants
| Thuộc tính | Giá trị |
|---|---|
| Source | public.Merchant (direct) |
| Config | src/configurations/merchant.collection.ts |
| Doc id | Merchant.id |
| queryBy | name.en, name.vi, description.en, description.vi, slug, identifier |
| Relations | organizer (native), parent (merchants, hydration) |
categories
| Thuộc tính | Giá trị |
|---|---|
| Source | public.Category (direct) |
| Config | src/configurations/category.collection.ts |
| Doc id | Category.id |
| queryBy | name.en, name.vi, description.en, description.vi |
| Relations | merchant (native) |
devices
| Thuộc tính | Giá trị |
|---|---|
| Source | public.Device (direct) |
| Config | src/configurations/device.collection.ts |
| Doc id | Device.id |
| queryBy | name.*, identifier, code |
| Relations | organizer (native), merchant (native) |
sale-channels
| Thuộc tính | Giá trị |
|---|---|
| Source | public.SaleChannel (direct) |
| Config | src/configurations/sale-channel.collection.ts |
| Doc id | SaleChannel.id |
| queryBy | name.en, name.vi, slug, identifier |
| Relations | merchant (native), parent (sale-channels, hydration) |
products
| Thuộc tính | Giá trị |
|---|---|
| Source | public.Product + public.ProductInfo (i18n) — cả hai direct (info là partial update) |
| 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 denormalized |
| Relations | merchant (native), parent (products, hydration) |
| Embedding | trường embedding từ [identifier, slug] |
product-variants
| Thuộc tính | Giá trị |
|---|---|
| Source | public.ProductVariant (direct) + 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 (native), merchant (native) |
inventories
| Thuộc tính | Giá trị |
|---|---|
| Source | inventory.InventoryStock (direct, doc grain) |
| 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[] (scheme-agnostic); flags[] (boolean dẫn xuất) |
| Notes | flags.isExpired / isExpiringSoon bị stale — refresh bởi reindex định kỳ hoặc range lúc-query |
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 soft-deleted ⇒ document vắng mặt | Mapper trả về null → CDCService xóa |
| Event CDC sai thứ tự không bao giờ ghi đè state mới hơn | Guard LSN (source_lsn) + so sánh deleted_at |
products.categoryIds phản ánh mọi dòng ProductCategory | Cascade fan-out + enrichment (không phải row mapper) |
Doc inventories là một-cho-mỗi-InventoryStock, item/location denormalized | Cascade từ InventoryItem/InventoryLocation |
Trường reference của Typesense phải ở top-level | Schema đặt merchantId, inventoryItemId, v.v. ở top level |
4. Hành vi Soft-delete
| Hành vi | Chi tiết |
|---|---|
| Nguồn chân lý | PostgreSQL deletedAt (do source service sở hữu) |
| Hiệu ứng search | Mapper phát hiện deletedAt != null → emit delete tới Typesense |
| Đọc mặc định | Dòng soft-deleted không có document, nên không bao giờ xuất hiện trong kết quả |
| Hard-delete | Một op Debezium d cũng xóa document |
| Restore | Một event CDC không-deleted sau đó re-upsert document |