Appearance
Runtime And Delivery — Business Rules
Tổng quan
Domain này gom phần notification chạy thật khi hệ thống đang hoạt động:
- inbox cá nhân của user
- web push listener trên FE
- vendor delivery cho push/email/SMS
- queue/scheduler cho ZNS và deferred tasks
- log/read model như
notification_user,message_user_view,sms_user,zns_user
Khác với config surface, domain này trả lời câu hỏi: "message được giao, đọc, log và reconcile như thế nào?"
Scope nghiệp vụ
| Nhóm | Vai trò | Surface chính |
|---|---|---|
| Personal inbox | Badge unread, notification list, mark read / read all | NotificationButton, NotificationList |
| Web push listener | Lắng nghe push hiện popup trong app | NotificationListener, useOneSignal, boot/onesignal.ts |
| Push delivery | Gửi notification qua vendor fcm / onesignal / none | notification-api/messenger |
| Email/SMS delivery | Save request hoặc send-now, log response | sendEmails, sendSMS, email_request_insert, sms_request_insert |
| ZNS + deferred tasks | Immediate send hoặc persist future task | zns_request_insert, scheduled_tasks, message scheduler |
Luồng chính
text
App boot
-> init OneSignal
-> nếu browser support push:
-> register push
-> bind externalUserId = account.id
-> attach notificationDisplay listener
User mở notification bell
-> query unread aggregate
-> load notification_personal(args)
-> xem item
-> upsert notification_user(read = true)
-> refresh badge count
Backend delivery
-> notification/email/sms/zns request vào DB hoặc action trực tiếp
-> service tương ứng chọn vendor / template / variables
-> send
-> update response + sent_at + success/failure stats
-> log user-level result vào các bảng/view phụQuy tắc nghiệp vụ
| Mã | Quy tắc |
|---|---|
| BR-NRT-001 | Inbox cá nhân không đọc trực tiếp từ bảng notification; nó đi qua function notification_personal(args) và stats function notification_personal_stats(args). |
| BR-NRT-002 | Badge unread count được tính bằng total_count - read_count, sau đó cache vào Pinia store + localStorage. |
| BR-NRT-003 | Mark-as-read không mutate bản ghi notification gốc; hệ thống upsert notification_user để lưu trạng thái đọc/xóa theo từng user. |
| BR-NRT-004 | Đọc tất cả là flow 2 bước: lấy toàn bộ unread IDs rồi bulk upsert notification_user(read = true). |
| BR-NRT-005 | FE web push hiện chỉ bind runtime service qua OneSignal; app khởi tạo listener từ boot/onesignal.ts. |
| BR-NRT-006 | Khi subscribe thành công, FE gắn externalUserId = account.id để map thiết bị/vendor user với account trong hệ thống. |
| BR-NRT-007 | Push payload runtime dùng subject_type + subject_id + data để resolve deep-link sang module đích; notification domain không tự sở hữu màn hình detail đích. |
| BR-NRT-008 | Backend notification-api chịu trách nhiệm cho push/email/SMS kiểu v1; notification-v2-api chịu ZNS, messenger integrations và scheduler runtime. |
| BR-NRT-009 | ZNS request có hai nhánh: gửi ngay nếu đến hạn gần, hoặc persist sang scheduled_tasks nếu còn xa. |
| BR-NRT-010 | scheduled_tasks không chỉ là DB queue; nhánh v2 còn có in-memory scheduler để hot-load task trong ngày và hỗ trợ hủy task khi status chuyển canceled. |
| BR-NRT-011 | Runtime log không thống nhất về một bảng duy nhất: push dùng notification_user, SMS dùng sms_user, ZNS dùng zns_user, view tổng hợp dùng message_user_view. |
Boundary
| Boundary | Ghi chú |
|---|---|
auth | runtime bind theo account/session/logout |
user / crm / ecommerce / wallet / projects | deep-link đích của notification nằm ở các module này |
settings | template/config quyết định runtime output nhưng không chạy delivery tại đây |
notification-api vs notification-v2-api | runtime bị chia thành hai thế hệ service song song |
Rủi ro / Findings
| Mức | Finding |
|---|---|
| P1 | Routing trong NotificationList dùng switch lớn trên NOTIFICATION_TYPE; đây là coupling trực tiếp từ notification runtime sang nhiều module khác. |
| P1 | Runtime status của admin UI đang nhìn theo send_after, trong khi delivery result thật lại nằm ở sent_at/response. |
| P1 | message_user_view chỉ là read model; muốn debug delivery phải lần ngược về từng bảng request/log family khác nhau. |
| P2 | notification runtime hiện song song 2 model recipient identity: notification_user trong DB và externalUserId bên OneSignal. |