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

394 lines
12 KiB
Markdown

# 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
```mermaid
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
```mermaid
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
```mermaid
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
```csharp
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
```mermaid
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
```csharp
// 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
```mermaid
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
```mermaid
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)
```yaml
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
```yaml
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
- [MinIO Documentation](https://min.io/docs/minio/)
- [Aliyun OSS Documentation](https://www.alibabacloud.com/help/oss)
- [Polly Resilience](https://github.com/App-vNext/Polly)
- [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)