ADR-0002. CDC (Debezium) làm seam tích hợp — Kafka chỉ producer
| Trường | Giá trị |
|---|---|
| Status | Accepted |
| Ngày | 2026-03-15 |
| Người quyết định | commerce-team, platform-team |
| Thay thế | — |
Bối cảnh
- Commerce là nguồn sự thật catalog/tenant. Nhiều dịch vụ cần dữ liệu của nó: search (index products/merchants/categories), inventory (seed locations + items), pricing (init fares), taxation (cấp phát nhóm thuế), invoice (suy ra
TaxInfo). - Produce Kafka cấp ứng dụng sẽ buộc commerce phải biết mọi hợp đồng downstream, phát một topic cho mỗi hình dạng thay đổi, và giữ code emit đồng bộ với mọi mutation catalog qua nhiều dịch vụ — gắn kết cao và dễ bỏ sót một đường ghi.
- Postgres đã ghi mọi thay đổi trong WAL. Debezium có thể capture nó một cách tổng quát.
Quyết định
Dùng CDC (Debezium) làm seam tích hợp chính. Nghĩa vụ duy nhất của commerce là ghi đúng vào Postgres; Debezium đọc WAL và publish một topic cho mỗi bảng (CDCKafkaTopics / CdcTables trong @nx/core, ví dụ public.Merchant, public.ProductVariant).
- Các consumer downstream (search/inventory/pricing/taxation/invoice) sở hữu đấu nối CDC consumer của riêng họ.
ApplicationKafkaComponentcủa commerce bind một producer thuần — và thực tế không gọi.send()ở bất cứ đâu trongsrc/. Một khóa bindingAPPLICATION_KAFKA_CONSUMERđược khai báo nhưng không có consumer nào được đấu nối.- CDC consumer duy nhất mà chính commerce chạy là
ApplicationCdcComponent(từ@nx/search, vai trò WORKER) để đồng bộ DB của chính nó vào Typesense. - Thông tin thuế merchant minh họa mẫu hình này: ghi vào
Merchant.metadata.tax, được capture bởi CDC, và được upsert vàoTaxInfobởi@nx/invoice(nguồn đọc downstream có thẩm quyền).
Hệ quả
| Ưu | Nhược |
|---|---|
| Không code emit theo từng consumer trong commerce; consumer mới gắn vào mà không cần đổi commerce | Consumer gắn với schema bảng — đổi tên cột là phá vỡ |
| Không có rủi ro "quên emit trên đường ghi này" | CDC thêm Debezium/Kafka Connect làm phụ thuộc vận hành |
| Thứ tự WAL + replay offset cho phân phối bền, có thể replay | Nhất quán cuối cùng; consumer phải idempotent theo PK |
| Producer giữ lại cho dùng tương lai mà không ép buộc ngay | Một producer được bind-nhưng-nhàn-rỗi có thể gây nhầm lẫn cho người mới (tài liệu hóa ở đây) |
Phương án đã cân nhắc
| Tùy chọn | Ưu | Nhược | Vì sao bị từ chối |
|---|---|---|---|
| Produce Kafka cấp ứng dụng theo từng thay đổi | Payload tường minh, được tuyển chọn | Gắn kết cao; phải instrument mọi đường ghi trong mọi dịch vụ | Dễ vỡ; phá vỡ tính đơn giản của nguồn sự thật |
| Fan-out HTTP đồng bộ trên mỗi mutation | Tức thì | Gắn kết runtime chặt; lỗi lan tỏa; ghi chậm | Bán kính tác động không chấp nhận được |
| Bảng outbox + relay | Đảm bảo transactional | Tái phát minh thứ Debezium cho miễn phí trên WAL | Debezium đã bao quát |
Tham khảo
packages/core/src/common/cdc/tables.ts(CdcTables)packages/core/src/common/kafka/topics.ts(CDCKafkaTopics)src/components/kafka/component.ts(producer được bind, không.send())src/common/keys.ts(APPLICATION_KAFKA_CONSUMERđược khai báo, không dùng)src/services/merchant.service.ts(tax →metadata.tax→ CDC →TaxInfo)