ADR-0002. Phát sự kiện chỉ qua WebSocket trong tiến trình, không Kafka
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-05-01 |
| Deciders | Phat Nguyen |
| Supersedes | - |
Bối cảnh
- Phần lớn service BANA phát Kafka event cho CDC và điều phối liên service.
- Outreach thu thập yêu cầu và đăng ký từ trang công khai. Phía tiêu thụ ở hạ nguồn duy nhất là BO dashboard, vốn muốn một thông báo nổi (toast) thời gian thực khi có yêu cầu mới.
- Không service nào khác cần phản ứng với một yêu cầu hay đăng ký như một sự kiện nghiệp vụ. Bản ghi lâu bền là dòng dữ liệu trong PostgreSQL.
Quyết định
Outreach không có Kafka producer, không có Kafka consumer, và không có BullMQ worker. Bề mặt bất đồng bộ duy nhất của nó là một WebSocketEmitter dựa trên Redis (outreach-ws-emitter), phát quảng bá INQUIRY_SUBMITTED tới các observation room khi một yêu cầu được gửi lên.
Việc phát đi theo kiểu gửi rồi quên: yêu cầu được ghi vào database trước, sau đó OutreachSocketEventService.notifyInquirySubmitted() chạy mà không chờ kết quả, được bảo vệ bởi isReady() và dùng Promise.allSettled trên các room. Một WS event bị rớt không bao giờ làm mất dữ liệu - BO có thể tải lại qua GET /inquiries.
Hệ quả
| Ưu | Nhược |
|---|---|
| Hạ tầng tối thiểu: không có Kafka topic, schema, consumer group, hay DLQ phải vận hành | Nếu sau này có service cần inquiry event, nó phải poll REST hoặc phải bổ sung một seam Kafka |
| Trải nghiệm BO thời gian thực qua Redis fan-out (hoạt động trên nhiều replica) | Việc gửi qua WS chỉ ở mức nỗ lực tối đa, không bảo đảm lâu bền |
| Service đơn giản hơn: ít chế độ lỗi hơn, phạm vi ảnh hưởng nhỏ hơn | Không nhất quán với chuẩn Kafka-CDC ở các nơi khác trong nền tảng |
| Database vẫn là nguồn dữ liệu đúng duy nhất | Phân tích trên các yêu cầu phải đọc DB, không phải một luồng event |
Các phương án đã cân nhắc
| Phương án | Ưu | Nhược | Lý do loại bỏ |
|---|---|---|---|
Phát Kafka inquiry.submitted | Lâu bền, fan-out tới bất kỳ consumer nào, hợp chuẩn nền tảng | Gánh nặng vận hành (topic, consumer, DLQ) cho một consumer nội bộ | Hiện chưa có consumer lâu bền nào |
Debezium CDC trên bảng Inquiry | Không cần code ứng dụng; ghi nhận mọi thao tác ghi | Hạ tầng nặng cho một bảng lưu lượng thấp; ràng buộc consumer vào schema | Không tương xứng với nhu cầu |
| Chờ WS notify ngay trong request | Đảm bảo gửi thông báo trước khi trả 201 | Ràng buộc độ trễ/khả dụng của thao tác gửi vào tình trạng của Redis | Thao tác gửi phải thành công ngay cả khi Redis sập |
Tham chiếu
packages/outreach/src/components/websocket/component.ts(ApplicationWebSocketComponent)packages/outreach/src/components/websocket/socket-event.service.ts(notifyInquirySubmitted)packages/outreach/src/application.ts(không cấu hình component Kafka/BullMQ nào)- API Events