On this page
Kho dữ liệu MSO Cloud là kiến trúc medallion (Bronze → Silver → Gold) phục vụ đồng thời nhiều plane dữ liệu có độ tin cậy khác nhau. Mọi con số trên dashboard truy được về đúng một tầng và đúng một helper.
Ba plane, một kho#
Ba plane dữ liệu không bao giờ trộn lẫn — trộn sai plane là lớp bug lặp lại nhiều nhất của hệ.
| Plane | Nguồn | Đích | Kiểm chứng |
|---|---|---|---|
| 1st-party marketplace — sự thật (money plane) | connector sàn + upload CSV/XLSX | Bronze → Silver → Gold agg_* |
verify:data — reconciliation neo vào oracle NGOÀI code |
| 1st-party marketing | ads / web / search / CRM | Bronze → Silver → Gold (semantics riêng, không blend với eCom) | verify:data (assertion riêng theo grain) |
| 3rd-party thị trường / social — ước tính | dữ liệu thị trường 3rd, social listening 3rd | store market_intel_* / social_signal_daily (bỏ qua Bronze) |
KHÔNG vào verify:data — badge "ước tính" |
Chỉ hai plane 1st-party đi qua medallion. Plane ước tính hội tụ về một persister riêng (persistMarketIntelSnapshot), supersede theo natural key, và chỉ vào hệ ra quyết định ở vai trò TÍN HIỆU — không bao giờ nuôi KPI 1st-party.
Bronze — thô, append-only, đã khử trùng lặp#
Tầng tiếp nhận. Mọi ingest (connector hoặc upload) đổ vào Bronze trước:
raw_events— append-only, mỗi row kýsha256(payload);ON CONFLICT DO NOTHINGkhử trùng lặp. Chỉ ba stream canonical:orders | creators | inventory.import_staging_rows— mọi stream phi-canonical (kho, payout, hoàn trả, chi phí quảng cáo, …). Bronze giữ đúng ba stream canonical; phần còn lại đợi promoter riêng.connector_watermarks—last_cursor+high_water_mark; chỉ tiến khi cả job thành công (một lần chạy lỗi KHÔNG làm tiến watermark).sync_rejected_rows— row rớt Zod validation.sync_dead_letters— lỗi permanent vào DLQ một lần, và tự lành khi một lần sync sau đó thành công.
Bronze là bất biến: chế độ replay re-derive lại Silver từ Bronze mà không phải trả phí gọi API nguồn lần nữa.
Silver — fact typed, re-derive được#
"Promoter" per-stream trong apps/worker/src/promoters/ chuyển Bronze thành fact typed: orders, order_items, creator_performance_daily, inventory_snapshots, … Promoter idempotent — chạy lại re-derive Silver một cách tất định từ Bronze. Thứ tự chạy có ý nghĩa: nội dung API phải được promote vào creator-daily TRƯỚC khi rollup creator chạy, nếu không rollup đọc thiếu.
Gold — KPI materialized, read-path của dashboard#
refreshOrgMaterializations tính các bảng agg_* — read-path nhanh mà dashboard và report đọc. Một số bảng tiêu biểu:
agg_org_day— KPI theo org theo ngày.agg_org_sku_day— theo SKU theo ngày.agg_org_creator_day— theo creator theo ngày.agg_content_dim_day— Audience CI, phân phối theo dimension.
Danh sách đầy đủ mở rộng theo pillar; mỗi bảng agg mới tuân cùng một hợp đồng key và refresh.
Mọi key của một bảng agg đều chứa brand_id. NULL brand hiển thị dưới MỌI brand, nên một write thiếu brand sẽ rò rỉ chéo brand; refresh brand-scoped DELETE theo (org_id, brand_id). Cube và tRPC chỉ đọc Gold (analytics ad-hoc nặng đi qua Cube đặt trên Silver/Gold).
Sơ đồ — luồng dữ liệu medallion#
flowchart TB
subgraph SRC["Nguồn"]
direction LR
MP["Marketplace API<br/>+ upload"]
MK["Marketing · CRM<br/>· Sheets"]
CT["Social listening<br/>(content)"]
TP["Thị trường 3rd"]
end
ING["Ingest — apps/worker<br/>rate-limit → Zod validation → dedupe sha256"]
subgraph BRZ["Bronze — append-only"]
direction LR
RAW["raw_events<br/>orders | creators | inventory"]
STG["import_staging_rows<br/>stream phi-canonical"]
end
PROM["Promoters — idempotent, re-derive tất định từ Bronze"]
SLV["Silver — fact typed<br/>orders · order_items · creator_performance_daily · inventory_snapshots"]
GLD["Gold — agg_* KPI, mọi key có brand_id<br/>refreshOrgMaterializations"]
SRV["Cube semantic layer · tRPC v11 · RSC"]
DASH["Dashboard / báo cáo"]
EST["market_intel_* · social_signal_daily<br/>badge: ước tính"]
MP --> ING
MK --> ING
CT --> ING
ING --> BRZ
BRZ --> PROM
PROM --> SLV
SLV --> GLD
GLD --> SRV
SRV --> DASH
TP -.->|"bỏ qua Bronze"| EST
EST -.->|"chỉ tín hiệu, không nuôi KPI 1st-party"| SRVPlane ước tính (market_intel_*) cắm thẳng vào store riêng, không đi qua Bronze, và luôn mang badge "ước tính". Nó ở ngoài verify:data theo thiết kế — một ước lượng thị trường không có oracle ngoài để đối chiếu, nên không được đặt cạnh con số 1st-party như thể cùng độ tin cậy.
Sơ đồ — thứ tự promoter#
flowchart TB A["raw_events / import_staging_rows"] B["promote orders → orders · order_items"] C["promote creators → creator_performance_daily"] D["promoteApiContentToCreatorDaily"] E["refreshAggOrgCreatorDay"] F["refreshOrgMaterializations → agg_*"] A --> B A --> C C --> D D --> E B --> F E --> F
Thứ tự là bất biến: nội dung API vào creator-daily TRƯỚC rollup creator; mọi rollup chạy trước khi materialize Gold.
Vì sao các con số đáng tin — các luật độ tin cậy#
Kho dữ liệu này khác một warehouse thông thường ở chỗ mỗi con số đều bị ràng buộc bởi một bất biến được cưỡng chế:
- VND là số nguyên 64-bit (bigint), không thập phân. Không bao giờ dùng
float/numericcho tiền; format qua một helper duy nhất. - Mọi ngày theo Asia/Ho_Chi_Minh (UTC+7), end-to-end. Con số trên màn hình PHẢI bằng con số khách tải về. Không convert UTC trên đường ngày; bucket SQL neo theo ICT — tránh rò biên ngày (nửa đêm UTC = 07:00 ICT kéo nhầm ngày tháng bên cạnh vào khoảng lọc).
is_sampleở khắp nơi. Mỗi bảng mang cờ này; seed một brand không được đụng brand khác. Dữ liệu mẫu và dữ liệu thật không bao giờ lẫn.- Một metric = một helper. Mỗi metric có đúng một định nghĩa sau đúng một hàm (ví dụ merge creator CSV/API là đúng một impl); mọi surface gọi cùng helper đó — không có đường tính thứ hai để lệch.
- Mỗi metric surface nợ một reconciliation assertion. Headline PHẢI bằng
SUM(dòng bảng render); rolling PHẢI bằng period cho cùng cửa sổ; không double-count khi merge. Giá trị EXPECTED neo vào oracle NGOÀI code (export thật của khách / ground-truth đóng băng), không bao giờ là một phép re-derive trong chính code. Một discrepancy không mặc nhiên là bug — phải xác nhận semantics với oracle trước khi "sửa".
Plane ước tính có bộ luật riêng: không có mẫu số bịa, không multiplier bịa; ở trần đọc thì render "≥ N" thay vì một con số cắt cụt.
Đọc thêm#
- architecture-detail.md — kiến trúc phân tầng chi tiết (Bronze → Silver → Gold trong ngữ cảnh toàn hệ).
- data-validation.md — hợp đồng kiểm chứng dữ liệu tự động.