Bỏ qua nội dung

Tổng quan

1. Trọng tâm thiết kế

  • Xây một pipeline ingest dữ liệu listing thời gian thực, ổn định và có kiểm soát.
  • Chuẩn hóa dữ liệu đa nguồn về một canonical schema thống nhất.
  • Khử trùng lặp thực thể (entity resolution), giữ được lịch sử nguồn và thay đổi.
  • Quản lý vòng đời listing bằng state machine nhất quán.
  • Phân phối listing theo rules + SLA tới đúng hub/broker/agent.
  • Bảo đảm tài chính và trạng thái quan trọng theo nguyên tắc atomic, idempotent, audit được.

2. 7 nguyên tắc bất biến (Architecture Principles)

7 nguyên tắc dưới đây là ràng buộc cứng cho mọi quyết định kỹ thuật trong VNexus. Bất kỳ sai khác nào đều phải có ADR phê duyệt bởi Tech Lead.

#Nguyên tắcHệ quả thiết kế
P1Data is the productMọi pipeline phải log raw, traceable, replayable. Không bao giờ ghi đè dữ liệu thô.
P2Listing ≠ PropertyTách rõ 2 entity. Một property có thể có N listing từ nhiều nguồn/môi giới.
P3Confidence over certaintyMọi field do AI/rule sinh ra đều phải có confidence_score [0..1] và evidence.
P4AI suggests, human confirmsTrong MVP, không AI nào được tự ý merge property, ban contact, hoặc xóa dữ liệu.
P5Idempotency by designMọi job, mọi POST/PATCH đều phải idempotent qua idempotency_key hoặc content_hash.
P6Audit everything sensitivePhone, address, legal docs, ownership change → bắt buộc audit log trước khi commit.
P7Schema-first, contract-firstDB migration & OpenAPI spec phải merge trước khi code business logic.

3. Vấn đề cần ưu tiên

  1. Độ đúng của dữ liệu listing:
    • Listing phải có vòng đời rõ ràng (submitted → validated → approved/flagged → published → dispatched → closed/expired).
    • Các chuyển trạng thái quan trọng phải atomic, không được nửa vời.
  2. Dữ liệu đa nguồn và trùng lặp:
    • Cùng một bất động sản có thể tới từ nhiều nguồn với format khác nhau.
    • Cần pipeline cleaning/standardization/matching/merge để tránh duplicate và sai lệch.
  3. Chuẩn hóa schema từ đầu:
    • Mọi nguồn map về canonical schema để không vỡ dữ liệu khi mở rộng.
  4. Phân phối vận hành theo SLA:
    • Routing listing theo geo/rule.
    • Timeout SLA thì re-route tự động.
  5. Realtime và minh bạch:
    • Dashboard admin cập nhật nhanh theo event.
    • Mọi thay đổi phải truy vết được qua audit log bất biến.

4. Sơ đồ tổng thể (architecture)

flowchart LR
    subgraph S[Sources]
        SA[API and Partner Feed]
        SU[Manual or User Input]
    end

    subgraph I[Ingestion Layer]
        IA[Source Adapters]
        IN[Schema Normalizer]
        IR[Entity Resolution]
        IT[Trust and Quality Engine]
    end

    subgraph C[Core Platform]
        CE[Event Log]
        CW[State Store]
        CR[Dispatch Rules Engine]
        CL[Ledger and Wallet]
        CA[Immutable Audit Log]
    end

    subgraph D[Read and Delivery]
        DP[Read Models and Search]
        DN[Notification Service]
        DG[WebSocket and REST API]
        DA[Admin Console]
        DH[Hub or Broker or Agent]
    end

    SA --> IA
    SU --> IA
    IA --> IN
    IN --> IR
    IR --> IT
    IT --> CE

    CE --> CW
    CE --> DP
    CE --> CR
    CW --> CL
    CW --> CA
    CW --> DN
    CR --> DH
    DN --> DG
    DP --> DA
    DG --> DA

5. Phân biệt rõ Listing vs Property — quy ước cốt lõi

Khía cạnhListing (tin đăng)Property (tài sản thật)
Bản chấtMột bản tin xuất hiện trên thị trường; có thể tạm thời, có thể lặp lạiMột bất động sản có thật ngoài đời
Tính duy nhất1 property → N listing (nhiều môi giới đăng cùng 1 căn)1 property = 1 record duy nhất sau khi merge
Vòng đờiActive → Inactive/Removed/Duplicated; có thể tái xuấtTồn tại lâu dài; chỉ đổi khi giao dịch hoặc thay đổi vật lý
Nguồnsource_id rõ (Chợ Tốt, Batdongsan, fanpage, manual, …)Không có nguồn — kết quả tổng hợp từ nhiều listing
Khi nào tạoMỗi lần ingest thành công → 1 raw_listingKhi listing match property cũ (đủ confidence) hoặc tạo property mới
Giáprice tại thời điểm đăng (listing_price_history)Không giá cố định — chỉ valuation_snapshot tham chiếu
Liên hệcontact_phone / contact_name của người đăng tinKhông gắn contact trực tiếp — qua contact_property_relations

Ví dụ minh họa: Một căn nhà ở Tân Bình (property X) có thể có 6 listing — 1 chính chủ trên Chợ Tốt, 3 môi giới trên batdongsan.com.vn ở 3 thời điểm, 1 fanpage sàn, 1 nhập tay từ broker nội bộ. Property Matching Service phải gom 6 listing về cùng property_id = X.

Feature liên quan: listing-create, properties-create, listing-crawl-external-sources.

6. Mô hình triển khai: Modular Monolith + Async Workers

Lý do không dùng microservices ngay từ đầu

  • Team nhỏ — microservices = overhead vận hành lớn, debug khó.
  • Chưa biết bounded context nào scale trước — chia sớm = chia sai.
  • Modular monolith vẫn cho tách service sau nếu mỗi module có DAO riêng, không cross-import service layer giữa module.

Ba process chính

ProcessVai trò
backend-apiModular monolith Java Spring Boot — mỗi domain một module/package; không gọi trực tiếp repository hoặc entity của module khác.
ai-serviceFastAPI riêng, REST internal; chung host hoặc container tách.
data-workerCrawler / parser / normalizer; job từ Redis queue (BullMQ).

Tất cả dùng chung 1 PostgreSQL; mỗi service chỉ truy cập schema/bảng domain của mình.

Hướng dẫn consistency (bổ sung)

  • Ingestion: adapter theo nguồn; idempotency, retry + backoff, validate schema ở biên (P5, P7).
  • Event-driven core: thay đổi listing → event bất biến; partition theo listing_id; replay được (P1).
  • Write path: listing state, dispatch, ledger/wallet commit atomic; outbox đồng bộ DB và bus.
  • Read path: projection cho dashboard/search; WebSocket snapshot + delta + sequence.