ADR-0002. Activity notification nạp từ Kafka, lưu trong core, rồi đẩy đi
| Trường | Giá trị |
|---|---|
| Status | Accepted |
| Date | 2026-05-20 |
| Deciders | Phat Nguyen |
| Supersedes | - |
Bối cảnh
- Sự kiện domain (ví dụ thanh toán thành công) cần trở thành notification bền vững, theo từng user, hiển thị trong UI "chuông" và kèm một tín hiệu báo trực tiếp qua WS.
- Producer biết điều gì đã xảy ra và một phạm vi người nhận (org / merchant / danh sách users cụ thể) nhưng không biết danh sách người nhận thực tế.
- Signal đã sở hữu điểm biên WebSocket nhưng không có schema; bảng
ActivityNotificationđược tập trung trong@nx/core. - Notification phải tồn tại được kể cả khi socket bị lỡ - client có thể đang offline lúc sự kiện phát sinh.
Quyết định
Chúng ta sẽ nạp activity notification vào Signal qua Kafka (signal.activity-notification), và để worker phân giải → lưu → đẩy đi:
- Tiêu thụ
TActivityNotificationMessage; từ chốieventTypelạ. - Phân giải người nhận ở phía server -
org/merchantquaPolicyDefinitionRepository,usersquarecipientIdscụ thể (mặc định lui về[actorId]). - Dựng
content+htmltừ một markdown decorator, lưu một hàngActivityNotificationcho mỗi người nhận quacreateAll. - Đẩy
observation/signal/notification/createdtới từng roomsignal/notification/{recipientId}.
Consumer chạy với autocommit: false, commit theo từng message sau khi handler trả về, và fallbackMode: latest.
Hệ quả
| Ưu | Nhược |
|---|---|
| Notification vừa bền vững (DB) vừa trực tiếp (WS) | Gửi lại sẽ tạo hàng trùng - hiện chưa khử trùng lặp |
| Producer chỉ cần lo phạm vi; Signal đảm nhận việc phân giải người nhận | Phân giải người nhận là một lần truy vấn DB đồng bộ cho mỗi message |
Tái dùng schema/repo tập trung của @nx/core | Lỗi handler chỉ được ghi log chứ không retry/DLQ |
| Commit thủ công đảm bảo an toàn ở mức ít nhất một lần | Hiện mới chỉ hiện thực nội dung cho PAYMENT_SUCCESS |
Phương án đã cân nhắc
| Phương án | Ưu | Nhược | Vì sao loại |
|---|---|---|---|
| Chỉ đẩy qua WS (không lưu) | Đơn giản nhất | Mất nếu client offline; không có lịch sử | UI chuông cần lịch sử bền vững |
| Producer tự phân giải người nhận + ghi hàng | Signal giữ đơn giản | Mỗi producer lặp lại việc phân giải + phải biết schema | Tập trung trong Signal đỡ trùng lặp hơn |
| Autocommit + idempotency key | Ít code thủ công | Cần kho lưu khử trùng lặp; chưa xây | Tạm hoãn; commit thủ công là phương án an toàn trung gian |
Tham chiếu
signal/src/components/notification/component.ts(cấu hình consumer)signal/src/services/activity-notification-worker.service.ts(phân giải/lưu/đẩy)core/src/common/kafka/types.ts(TActivityNotificationMessage)- API Events - idempotency