Files
pos-system/services/storage-service-net/docs/vi/ARCHITECTURE.md
Ho Ngoc Hai 5c8764f63a docs(architecture): Update documentation for direct upload architecture and API endpoints
- Enhanced the architecture documentation to recommend direct upload over legacy proxy upload for improved performance and scalability.
- Added detailed comparisons of upload patterns, including throughput, memory usage, and latency.
- Updated API endpoint documentation to reflect new direct upload methods and their benefits.
- Included examples for direct upload flow and bucket directory structure to aid developers in implementation.
2026-01-13 21:17:55 +07:00

12 KiB

Kiến Trúc Storage Service

Tài liệu kiến trúc chi tiết cho microservice Storage Service.

Tổng Quan Kiến Trúc

graph TB
    subgraph "API Layer"
        C[Controllers]
        CMD[Commands]
        Q[Queries]
        B[Behaviors]
    end
    
    subgraph "Domain Layer"
        SF[StorageFile]
        SQ[UserStorageQuota]
        DE[Domain Events]
        RI[Repository Interfaces]
    end
    
    subgraph "Infrastructure Layer"
        SP[Storage Providers]
        IAM[IAM Client]
        R[Repositories]
        CTX[DbContext]
    end
    
    subgraph "External Services"
        MINIO[(MinIO)]
        OSS[(Aliyun OSS)]
        IAMS[IAM Service]
        DB[(PostgreSQL)]
    end
    
    C --> CMD
    C --> Q
    CMD --> B --> SF
    Q --> R
    R --> CTX --> DB
    SF --> DE
    
    CMD --> SP
    SP --> MINIO
    SP --> OSS
    
    C --> IAM --> IAMS
    
    style C fill:#4a90d9,stroke:#2d5986,color:#fff
    style SF fill:#50c878,stroke:#2d8659,color:#fff
    style MINIO fill:#c73b3b,stroke:#922b2b,color:#fff
    style OSS fill:#ff6b35,stroke:#cc5500,color:#fff
    style IAMS fill:#9b59b6,stroke:#7d3c98,color:#fff

Trách Nhiệm Từng Layer

1. Domain Layer (StorageService.Domain)

Trái tim của ứng dụng chứa business logic thuần túy.

Component Mục đích
StorageFile Aggregate root cho metadata file và lifecycle
UserStorageQuota Aggregate root cho giới hạn và usage storage của user
StorageProvider Enum: MinIO, AliyunOSS
FileAccessLevel Enum: Private, Public, Shared
Domain Events FileUploadedDomainEvent, FileDeletedDomainEvent, UserQuotaUpdatedDomainEvent

2. Infrastructure Layer (StorageService.Infrastructure)

Triển khai kỹ thuật và tích hợp bên ngoài:

Component Mục đích
MinioStorageProvider Thao tác storage tương thích MinIO S3
AliyunOssStorageProvider Thao tác Alibaba Cloud OSS
StorageProviderFactory Chọn provider runtime dựa trên config
HttpIamServiceClient Giao tiếp inter-service với IAM
FileRepository EF Core repository cho StorageFile
QuotaRepository EF Core repository cho UserStorageQuota

3. API Layer (StorageService.API)

Entry point ứng dụng và triển khai CQRS:

Component Mục đích
FilesController Endpoints CRUD file (proxy upload cũ)
SignedUrlController Endpoints direct upload (khuyến nghị)
QuotaController Endpoints quota của user
SignUploadCommand Tạo pre-signed upload URLs
ConfirmUploadCommand Xác nhận direct uploads và lưu metadata
UploadFileCommand Xử lý proxy uploads (cũ)
DeleteFileCommand Xử lý xóa file
Query Handlers Xử lý các thao tác đọc

Kiến Trúc Direct Upload (Khuyến Nghị)

Cho hệ thống với hàng triệu users, pattern Direct Client Upload được khuyến nghị thay vì proxy upload.

So Sánh Upload Patterns

Khía cạnh Proxy Upload (Cũ) Direct Upload (Khuyến nghị)
Throughput ~100-500/giây ~10,000+/giây
Memory mỗi request 100MB (kích thước file) ~10KB (chỉ metadata)
Latency (file 100MB) 30-60 giây 10-20 giây
Tải backend Cao Tối thiểu

Luồng Direct Upload

sequenceDiagram
    participant Client
    participant Storage_Service as Storage Service
    participant MinIO
    
    rect rgb(200, 230, 200)
        Note over Client,Storage_Service: 1. Yêu cầu Upload URL (nhẹ)
        Client->>Storage_Service: POST /api/v1/storage/sign-upload
        Storage_Service->>Storage_Service: Validate JWT, Kiểm tra Quota
        Storage_Service-->>Client: Pre-signed PUT URL + ObjectKey
    end
    
    rect rgb(200, 200, 230)
        Note over Client,MinIO: 2. Upload trực tiếp (bỏ qua backend)
        Client->>MinIO: PUT file binary vào Pre-signed URL
        MinIO-->>Client: 200 OK
    end
    
    rect rgb(230, 230, 200)
        Note over Client,Storage_Service: 3. Xác nhận Upload (nhẹ)
        Client->>Storage_Service: POST /api/v1/storage/confirm-upload
        Storage_Service->>MinIO: Xác minh file tồn tại
        Storage_Service->>Storage_Service: Lưu metadata, Cập nhật quota
        Storage_Service-->>Client: File metadata
    end

Kiểm Soát Truy Cập Theo Path

Files được tổ chức với prefix theo access level:

storage-bucket/
├── public/{userId}/{date}/{fileId}_{filename}   → Truy cập công khai
├── private/{userId}/{date}/{fileId}_{filename}  → Yêu cầu pre-signed URL
└── shared/{userId}/{date}/{fileId}_{filename}   → Kiểm soát bằng quy tắc

Components Direct Upload

Component Mục đích
SignUploadCommand Validate quota, tạo object key với path prefix, tạo pre-signed PUT URL
SignUploadCommandHandler Xử lý yêu cầu sign-upload
ConfirmUploadCommand Xác minh file tồn tại, lưu metadata, cập nhật quota
ConfirmUploadCommandHandler Xử lý confirm-upload với idempotency
SignedUrlController Endpoints /sign-upload/confirm-upload

Kiến Trúc Storage Provider

graph TD
    subgraph "Storage Provider Factory"
        F[StorageProviderFactory]
        C[Configuration]
    end
    
    subgraph "Providers"
        MP[MinioStorageProvider]
        AP[AliyunOssStorageProvider]
    end
    
    subgraph "Storage Backends"
        MINIO[(MinIO)]
        OSS[(Aliyun OSS)]
    end
    
    C --> |STORAGE_PROVIDER=minio| F
    C --> |STORAGE_PROVIDER=aliyun| F
    F --> |GetProvider| MP
    F --> |GetProvider| AP
    MP --> MINIO
    AP --> OSS
    
    style F fill:#4a90d9,stroke:#2d5986,color:#fff
    style MP fill:#c73b3b,stroke:#922b2b,color:#fff
    style AP fill:#ff6b35,stroke:#cc5500,color:#fff

Interface Storage Provider

public interface IStorageProvider
{
    Task<UploadResult> UploadAsync(Stream stream, string objectKey, ...);
    Task<Stream> DownloadAsync(string bucketName, string objectKey);
    Task DeleteAsync(string bucketName, string objectKey);
    Task<bool> ExistsAsync(string bucketName, string objectKey);
    Task<string> GetPreSignedDownloadUrlAsync(string bucketName, string objectKey, int expirationSeconds);
    Task<string> GetPreSignedUploadUrlAsync(string bucketName, string objectKey, int expirationSeconds);
}

Giao Tiếp Inter-Service

sequenceDiagram
    participant Client
    participant Storage as Storage Service
    participant Cache as In-Memory Cache
    participant IAM as IAM Service
    
    Client->>Storage: Upload File (JWT)
    Storage->>Cache: Kiểm tra cache user
    alt Cache Hit
        Cache-->>Storage: Thông tin user
    else Cache Miss
        Storage->>IAM: GET /api/v1/users/me
        Note over Storage,IAM: Headers: Authorization, X-Service-Name
        IAM-->>Storage: Thông tin user
        Storage->>Cache: Lưu (5 phút TTL)
    end
    Storage->>Storage: Validate quota
    Storage->>Storage: Upload lên provider
    Storage-->>Client: Kết quả upload

Tính Năng IAM Client

Tính năng Mô tả
Caching In-memory cache cho user info (5 phút TTL)
Health Check Kiểm tra IAM availability với caching (1 phút TTL)
Polly Resilience Retry (3x exponential) + Circuit Breaker
Permission Check HasPermissionAsync, HasRoleAsync

Các Phương Thức IAM Client

// Thao tác User
Task<IamUserInfo?> ValidateUserAsync(string accessToken);
Task<IamUserInfo?> GetUserByIdAsync(string userId, string accessToken);
Task<bool> UserExistsAsync(string userId, string accessToken);
Task<IReadOnlyList<string>> GetUserRolesAsync(string userId, string accessToken);
Task<IReadOnlyList<string>> GetUserPermissionsAsync(string userId, string accessToken);
Task<bool> HasPermissionAsync(string userId, string permission, string accessToken);
Task<bool> HasRoleAsync(string userId, string role, string accessToken);

// Health Check
Task<IamHealthStatus> CheckHealthAsync();
Task<bool> IsAvailableAsync();

// Quản lý Cache
void InvalidateUserCache(string userId);
void ClearCache();

Database Schema

erDiagram
    storage_files {
        uuid id PK
        varchar file_name
        varchar bucket_name
        varchar object_key
        varchar content_type
        bigint file_size_bytes
        varchar user_id
        varchar tenant_id
        int provider
        int access_level
        timestamp uploaded_at
        timestamp expires_at
        varchar checksum
        boolean is_deleted
        timestamp deleted_at
    }
    
    user_storage_quotas {
        uuid id PK
        varchar user_id UK
        bigint max_storage_bytes
        bigint used_storage_bytes
        int max_file_count
        int current_file_count
        varchar quota_tier
        timestamp last_updated_at
        timestamp created_at
    }

API Endpoints

Files (Proxy Upload Cũ)

Method Endpoint Mô tả
POST /api/v1/files/upload Upload file qua backend (tối đa 100MB)
GET /api/v1/files Danh sách file với phân trang
GET /api/v1/files/{id} Lấy metadata file
GET /api/v1/files/{id}/download-url Lấy pre-signed download URL
DELETE /api/v1/files/{id} Xóa file (soft delete)

Direct Upload (Khuyến Nghị)

Method Endpoint Mô tả
POST /api/v1/storage/sign-upload Lấy pre-signed PUT URL để upload trực tiếp
POST /api/v1/storage/confirm-upload Xác nhận upload và lưu metadata

Quota

Method Endpoint Mô tả
GET /api/v1/quota Lấy quota storage của user

Health Checks

graph TD
    HC[Health Check Endpoint]
    HC --> |/health/live| L[Liveness]
    HC --> |/health/ready| R[Readiness]
    
    R --> DB[(PostgreSQL)]
    R --> MINIO[(MinIO)]
    R --> IAM[IAM Service]
    
    style HC fill:#3498db,stroke:#2980b9,color:#fff
    style L fill:#2ecc71,stroke:#27ae60,color:#fff
    style R fill:#f39c12,stroke:#d68910,color:#fff

Kiến Trúc Deployment

Docker Compose (Local)

services:
  storage-api:
    build: .
    ports: ["5002:8080"]
    depends_on:
      - postgres
      - redis
      - minio
    environment:
      - Storage__Provider=minio
      - Storage__MinIO__Endpoint=minio:9000
  
  minio:
    image: minio/minio:latest
    ports: ["9000:9000", "9001:9001"]

Tích Hợp Traefik

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.storage-service.rule=PathPrefix(`/api/v1/files`) || PathPrefix(`/api/v1/quota`)"
  - "traefik.http.services.storage-service.loadbalancer.server.port=8080"

Cân Nhắc Bảo Mật

  1. Authentication: Xác thực JWT Bearer qua IAM Service
  2. Authorization: Kiểm tra quyền sở hữu trên files
  3. Input Validation: Giới hạn kích thước file, xác thực content type
  4. Pre-signed URLs: Truy cập file có thời hạn
  5. Soft Delete: Files được đánh dấu xóa, không xóa ngay lập tức

Tối Ưu Hiệu Suất

  1. Caching: In-memory cache cho IAM user info
  2. Pre-signed URLs: Client download trực tiếp từ storage
  3. Streaming Upload: Xử lý file dựa trên stream
  4. Async Operations: Tất cả thao tác I/O đều async
  5. Connection Pooling: HTTP client với Polly policies

Tài Liệu Tham Khảo