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

11 KiB

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

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:

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

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

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

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

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

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

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+)

// 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

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

builder.Services.AddHealthChecks()
    .AddNpgSql(connectionString, name: "postgresql")
    .AddRedis(redisConnectionString, name: "redis")
    .AddSignalRHub<ChatHub>(name: "signalr-hub");

Tài Liệu Tham Khảo