Architecture
1. Bối cảnh hệ thống (C4 L1)
2. Góc nhìn Container (C4 L2)
3. Góc nhìn Component (C4 L3) — Phân lớp nội bộ
| Lớp | Trách nhiệm |
|---|---|
| Routes | Bề mặt HTTP trong RestPaths (/socket/websocket/clients, /notifications) + WS /stream |
| Controllers | Cổng auth + permission + phạm vi người nhận (userId từ JWT) |
| Services | Façade messaging WS, CRUD notification, xử lý message Kafka |
| Repositories | re-export @nx/core; PolicyDefinitionRepository giải người nhận |
| Components | Xuyên suốt: WebSocket server, Redis, ECDH, Kafka consumer |
4. Chỉ mục máy trạng thái
Signal không có entity domain stateful sống lâu. Gần nhất là vòng đời kết nối WebSocket ngắn ngủi và trạng thái đã đọc (nhị phân) của notification.
| Entity | Trạng thái | Sơ đồ |
|---|---|---|
| Kết nối WebSocket | CONNECTING, AUTHENTICATED, ENCRYPTED, CLOSED | → nhảy tới |
ActivityNotification.isRead | UNREAD, READ | → nhảy tới |
Kết nối WebSocket
| Từ | Sự kiện | Đến | Điều kiện |
|---|---|---|---|
CONNECTING | authenticate | AUTHENTICATED | type=Bearer + JWT hợp lệ qua JWKSVerifierTokenService |
AUTHENTICATED | handshake | ENCRYPTED | có clientPublicKey → derive + lưu khoá AES |
ENCRYPTED | disconnect | CLOSED | khoá AES xoá khỏi map _clientAesKeys |
Trạng thái đọc Notification
| Từ | Sự kiện | Đến | Điều kiện |
|---|---|---|---|
UNREAD | mark-read | READ | recipientId phải khớp subject JWT; đặt isRead=true, readAt=now() |
5. Kịch bản runtime
5.1 Kết nối WebSocket mã hoá
| Bước | Chi tiết |
|---|---|
| 2 | Trường payload là clientPublicKey; requireEncryption: true từ chối kết nối thiếu nó |
| 5 | Cặp khoá ephemeral theo từng client → forward secrecy; khoá chỉ sống trong bộ nhớ |
5.2 Pipeline Activity-Notification
| Bước | Chi tiết |
|---|---|
| 3 | org → findUserIdsInOrganizer; merchant → findUserIdsInMerchant; users → recipientIds rõ ràng; fallback [actorId] |
| 8 | Commit theo từng message sau khi handler return; lỗi on-message được log, không retry |
5.3 Cross-Service Push
| Bước | Chi tiết |
|---|---|
| 1 | Producer không host WS server — họ chỉ publish; signal là edge duy nhất |
| 3 | sendToClient deliver cục bộ nếu có, không thì re-publish tới instance sở hữu |
6. Mối quan tâm xuyên suốt
| Mối quan tâm | Cách service này xử lý |
|---|---|
| AuthN (REST) | Chiến lược JWT + Basic, xác minh qua JWKS từ xa (VerifierApplication) |
| AuthN (WS) | Token type=Bearer xác minh bởi JWKSVerifierTokenService trong handler authenticate |
| AuthZ | Permission Casbin WebSocketClient.* trên route quản lý client; /notifications phạm vi người nhận (không permission rõ ràng, scope theo subject JWT) |
| Encryption | ECDH P-256 + HKDF-SHA-256 + AES-256-GCM, khoá theo từng client trong map _clientAesKeys |
| i18n | Nội dung notification dựng phía server (hiện template tiếng Việt cho PAYMENT_SUCCESS) |
| Logging | key-value có cấu trúc (key: %s); Kafka consumer log topic/partition/offset |
| Idempotency | Không có ở consumer — at-least-once; re-delivery sẽ tạo hàng trùng (xem API Events) |
| Room ACL | validateRoom hiện là passthrough (chấp nhận mọi room); ACL phạm vi merchant là TODO được ghi |
| IDs | Snowflake qua IdGenerator, worker 9 |