Files
pos-system/services/chat-service-net/docs/vi/ARCHITECTURE.md

388 lines
11 KiB
Markdown

# Tài Liệu Kiến Trúc Chat Service
> Kiến trúc chi tiết cho Chat Service với E2EE, SignalR, scalability patterns, và AI integration.
## Tổng Quan Kiến Trúc
```mermaid
graph TB
subgraph "Clients"
WEB[Web App]
MOB[Mobile App]
BOT[Bot Client]
end
subgraph "Load Balancer"
LB[NGINX/Traefik]
SS[Sticky Sessions]
end
subgraph "Chat Service Instances"
CS1[Instance 1<br/>SignalR Hub]
CS2[Instance 2<br/>SignalR Hub]
CS3[Instance 3<br/>SignalR Hub]
end
subgraph "Backplane"
RD[(Redis<br/>Pub/Sub)]
end
subgraph "Storage"
PG[(PostgreSQL<br/>Messages)]
RC[(Redis<br/>Cache)]
end
subgraph "AI Service"
AI[OpenAI<br/>GPT-4]
end
WEB & MOB & BOT --> LB
LB --> SS --> CS1 & CS2 & CS3
CS1 & CS2 & CS3 <--> RD
CS1 & CS2 & CS3 --> PG
CS1 & CS2 & CS3 --> RC
CS1 & CS2 & CS3 --> AI
style LB fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:3px
style RD fill:#C0392B,color:#ECF0F1,stroke:#A93226,stroke-width:2px
style PG fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
style AI fill:#8E44AD,color:#ECF0F1,stroke:#7D3C98,stroke-width:2px
```
## Mã Hóa Đầu-cuối (E2EE)
### Giao Thức Trao Đổi Khóa X3DH
Service triển khai giao thức Extended Triple Diffie-Hellman (X3DH) để thiết lập phiên mã hóa:
```mermaid
sequenceDiagram
participant Alice as Alice (Người gửi)
participant Server as Chat Server
participant Bob as Bob (Người nhận)
Note over Bob,Server: Bob đăng ký keys
Bob->>Server: Đăng ký Key Bundle<br/>(Identity, SignedPreKey, OneTimePreKeys)
Server->>Server: Lưu chỉ public keys
Note over Alice,Server: Alice khởi tạo session
Alice->>Server: Yêu cầu Key Bundle của Bob
Server->>Alice: {IdentityKey, SignedPreKey, OneTimePreKey}
Alice->>Alice: X3DH → Session Key
Alice->>Alice: AES-256-GCM mã hóa tin nhắn
Alice->>Server: Gửi tin nhắn đã mã hóa
Server->>Bob: Chuyển tin nhắn đã mã hóa
Bob->>Bob: X3DH → Session Key
Bob->>Bob: AES-256-GCM giải mã tin nhắn
style Server fill:#2C3E50,color:#ECF0F1,stroke:#34495E,stroke-width:3px
```
### Các Thành Phần Khóa
| Thành phần | Mô tả | Lưu trữ |
|------------|-------|---------|
| **Identity Key** | Cặp khóa Curve25519 dài hạn | Private: Chỉ client |
| **Signed Pre-Key** | Xoay vòng mỗi 30 ngày | Public: Server |
| **One-Time Pre-Keys** | Tiêu thụ mỗi session | Public: Server (xóa sau khi dùng) |
| **Session Key** | Derive qua X3DH | Không lưu trữ |
### Tính Năng Bảo Mật
-**Forward Secrecy**: Khóa bị lộ không tiết lộ tin nhắn cũ
-**Zero-Knowledge Server**: Server không thể giải mã tin nhắn
-**Deniability**: Không có bằng chứng mật mã về tác giả
## Domain Model
### Aggregate Roots
```mermaid
erDiagram
Conversation ||--o{ Message : contains
Conversation ||--o{ ConversationParticipant : has
ChatUser ||--o{ Conversation : participates
ChatUser ||--|| UserKeyBundle : has
ChatUser ||--o{ OneTimePreKey : owns
Conversation {
uuid Id PK
string Name
string AvatarUrl
enum Type "Direct|Group"
uuid CreatorId
datetime CreatedAt
datetime LastActivityAt
}
Message {
uuid Id PK
uuid ConversationId FK
uuid SenderId FK
string EncryptedContent
string Nonce
string AuthTag
enum Type "Text|Image|File|System"
enum Status "Sent|Delivered|Read|Failed"
datetime CreatedAt
}
ConversationParticipant {
uuid Id PK
uuid ConversationId FK
uuid ChatUserId FK
enum Role "Owner|Admin|Member"
datetime JoinedAt
datetime LastReadAt
}
ChatUser {
uuid Id PK
string IdentityUserId
string DisplayName
string AvatarUrl
enum Status "Online|Away|Offline"
datetime LastSeenAt
}
UserKeyBundle {
string IdentityPublicKey
string SignedPreKey
string SignedPreKeySignature
datetime SignedPreKeyTimestamp
}
OneTimePreKey {
uuid Id PK
int KeyId
string PublicKey
bool IsUsed
datetime CreatedAt
}
```
### Domain Events
| Event | Trigger | Handler |
|-------|---------|---------|
| `ConversationCreatedDomainEvent` | Hội thoại mới được tạo | Thông báo participants |
| `MessageSentDomainEvent` | Tin nhắn được gửi | Cập nhật last_activity, broadcast |
| `MessageDeliveredDomainEvent` | Tin nhắn đã gửi đến | Thông báo sender |
| `MessageReadDomainEvent` | Tin nhắn đã đọc | Thông báo sender |
| `UserJoinedRoomDomainEvent` | User tham gia room | Thông báo members |
| `UserLeftRoomDomainEvent` | User rời room | Thông báo members |
| `TypingDomainEvent` | User đang gõ | Broadcast to room |
| `ChatUserCreatedDomainEvent` | Chat user mới | Khởi tạo presence |
| `UserKeyBundleUpdatedDomainEvent` | Keys được cập nhật | Invalidate cached keys |
## SignalR Hub Architecture
### Connection Lifecycle
```mermaid
sequenceDiagram
participant Client
participant Hub as ChatHub
participant Groups
participant DB as Database
participant Redis
Client->>Hub: Connect (JWT Token)
Hub->>Hub: OnConnectedAsync()
Hub->>DB: Load danh sách hội thoại
Hub->>Groups: AddToGroupAsync(conversationId)
Hub->>Redis: Publish(UserOnline)
Hub-->>Client: Connected
Note over Client,Hub: User hiện đang online
Client->>Hub: SendMessage(conversationId, encryptedContent)
Hub->>DB: Lưu tin nhắn đã mã hóa
Hub->>Redis: Publish(NewMessage)
Redis-->>Hub: Broadcast đến tất cả instances
Hub->>Groups: Clients.Group(conversationId)
Hub-->>Client: ReceiveMessage
style Hub fill:#2C3E50,color:#ECF0F1,stroke:#34495E,stroke-width:3px
style Redis fill:#C0392B,color:#ECF0F1,stroke:#A93226,stroke-width:2px
style DB fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
```
### Hub Methods
```csharp
public class ChatHub : Hub<IChatHubClient>
{
// Quản lý kết nối
public override Task OnConnectedAsync();
public override Task OnDisconnectedAsync(Exception? exception);
// Quản lý phòng
public Task JoinRoom(Guid conversationId);
public Task LeaveRoom(Guid conversationId);
// Nhắn tin
public Task SendMessage(Guid conversationId, string encryptedContent,
string nonce, string? authTag);
public Task SendTypingIndicator(Guid conversationId, bool isTyping);
public Task MarkMessageRead(Guid conversationId, Guid messageId);
// Tích hợp AI
public IAsyncEnumerable<string> StreamAIResponse(Guid conversationId,
string prompt);
}
```
## Scalability Architecture
### Redis Backplane
```mermaid
graph LR
subgraph "Instance 1"
C1[Client A] --> H1[Hub 1]
end
subgraph "Instance 2"
C2[Client B] --> H2[Hub 2]
end
subgraph "Redis"
CH[Channel: chat.messages]
end
H1 -->|Publish| CH
CH -->|Subscribe| H2
H2 -->|Deliver| C2
style CH fill:#C0392B,color:#ECF0F1,stroke:#A93226,stroke-width:2px
style H1 fill:#2C3E50,color:#ECF0F1,stroke:#34495E,stroke-width:2px
style H2 fill:#2C3E50,color:#ECF0F1,stroke:#34495E,stroke-width:2px
```
### Cấu Hình Scaling
| Option | Ưu điểm | Nhược điểm |
|--------|---------|------------|
| **Redis Backplane** | Đơn giản, on-premise | Cần quản lý Redis cluster |
| **Azure SignalR** | Serverless, không sticky sessions | Vendor lock-in, chi phí |
| **Sticky Sessions** | Đơn giản nhất | Không hoàn hảo cho failover |
## AI Integration Flow
```mermaid
sequenceDiagram
participant User
participant Hub as ChatHub
participant AI as AIService
participant OpenAI
participant DB as Database
User->>Hub: "@gpt Giải thích DDD"
Hub->>DB: Load lịch sử (20 tin nhắn)
Hub->>AI: StreamAsync(prompt, history)
AI->>OpenAI: ChatCompletion (stream: true)
loop Streaming
OpenAI-->>AI: Token chunk
AI-->>Hub: yield chunk
Hub-->>User: ReceiveAIChunk
end
Hub->>DB: Lưu AI response
Hub-->>User: AIResponseComplete
style Hub fill:#2C3E50,color:#ECF0F1,stroke:#34495E,stroke-width:3px
style AI fill:#8E44AD,color:#ECF0F1,stroke:#7D3C98,stroke-width:2px
style OpenAI fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
```
## Resiliency Patterns
### Automatic Reconnect
```mermaid
stateDiagram-v2
[*] --> Connected
Connected --> Disconnected: Mất kết nối
Disconnected --> Reconnecting: Tự động retry
Reconnecting --> Connected: Thành công
Reconnecting --> Reconnecting: Retry với backoff
Reconnecting --> Disconnected: Vượt quá max retries
Disconnected --> [*]: User logout
```
### Stateful Reconnect (.NET 8+)
```csharp
// Server configuration
builder.Services.AddSignalR(options =>
{
options.StatefulReconnectBufferSize = 32 * 1024; // 32KB
});
// Với UseStatefulReconnect
app.MapHub<ChatHub>("/chatHub", options =>
{
options.AllowStatefulReconnects = true;
});
```
## Deployment Architecture
### Kubernetes với Sticky Sessions
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: chatservice
spec:
replicas: 3
template:
spec:
containers:
- name: chatservice
image: chatservice:latest
ports:
- containerPort: 8080
env:
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: chat-secrets
key: redis-url
---
apiVersion: v1
kind: Service
metadata:
name: chatservice
annotations:
service.beta.kubernetes.io/aws-load-balancer-sticky-sessions: "true"
spec:
type: LoadBalancer
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 3600
ports:
- port: 80
targetPort: 8080
```
## Health Checks
```csharp
builder.Services.AddHealthChecks()
.AddNpgSql(connectionString, name: "postgresql")
.AddRedis(redisConnectionString, name: "redis")
.AddSignalRHub<ChatHub>(name: "signalr-hub");
```
## Tài Liệu Tham Khảo
- [ASP.NET Core SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/)
- [SignalR Scale-out with Redis](https://docs.microsoft.com/en-us/aspnet/core/signalr/redis-backplane)
- [X3DH Protocol Specification](https://signal.org/docs/specifications/x3dh/)
- [Azure SignalR Service](https://docs.microsoft.com/en-us/azure/azure-signalr/)