Files
pos-system/apps/app-client-base-swift/docs/vi/architecture.md

12 KiB

Hướng Dẫn Kiến Trúc

Tài liệu kiến trúc chi tiết cho ứng dụng iOS AppClientBaseSwift.

Tổng Quan

AppClientBaseSwift là ứng dụng iOS native được xây dựng theo mẫu kiến trúc MVVM (Model-View-ViewModel) với SwiftUI cho UI declarative. Ứng dụng tuân theo các best practices phát triển hiện đại của Apple bao gồm:

  • Swift Concurrency (async/await)
  • Combine cho reactive data binding
  • Protocol-oriented programming để tăng khả năng test
  • Keychain Services cho lưu trữ bảo mật

Sơ Đồ Kiến Trúc

┌─────────────────────────────────────────────────────────────────────────┐
│                          LỚP PRESENTATION                                │
│  ┌─────────────────────────────────────────────────────────────────────┐ │
│  │                         SwiftUI Views                                │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐   │ │
│  │  │ SplashView  │ │  HomeView   │ │ ExploreView │ │ ProfileView │   │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘   │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐                   │ │
│  │  │  LoginView  │ │RegisterView │ │ForgotPasswd │                   │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘                   │ │
│  └─────────────────────────────────────────────────────────────────────┘ │
│                        @StateObject / @EnvironmentObject                 │
│  ┌─────────────────────────────────────────────────────────────────────┐ │
│  │                        ViewModels (@MainActor)                       │ │
│  │  ┌────────────────┐ ┌────────────────┐ ┌────────────────┐           │ │
│  │  │ AuthViewModel  │ │ HomeViewModel  │ │ProfileViewModel│           │ │
│  │  └────────────────┘ └────────────────┘ └────────────────┘           │ │
│  └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
                    Dependency Injection (dựa trên Protocol)
┌─────────────────────────────────────────────────────────────────────────┐
│                            LỚP SERVICE                                   │
│  ┌─────────────────────────────┐  ┌─────────────────────────────┐       │
│  │        APIService           │  │       AuthManager            │       │
│  │  • request<T>()             │  │  • @Published authState      │       │
│  │  • get(), post()            │  │  • login(), register()       │       │
│  │  • URLSession               │  │  • Lưu trữ Keychain          │       │
│  └─────────────────────────────┘  └─────────────────────────────┘       │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│                             LỚP DATA                                     │
│  ┌─────────────────────────────┐  ┌─────────────────────────────┐       │
│  │          Models             │  │        Constants             │       │
│  │  • User (Codable)           │  │  • APIConfig                 │       │
│  │  • HomeItem                 │  │  • StorageKeys               │       │
│  │  • AuthState                │  │  • DesignSystem              │       │
│  └─────────────────────────────┘  └─────────────────────────────┘       │
└─────────────────────────────────────────────────────────────────────────┘

Chi Tiết Component

1. Lớp Presentation

Views

Component Trách nhiệm
SplashView Màn hình splash động, điều hướng trễ
ContentView Container TabView gốc, routing theo auth state
AuthContainerView Điều hướng luồng Auth (Login/Đăng ký/Quên MK)
HomeView Tab Home với lời chào, promo, dịch vụ
ExploreView Tính năng khám phá và tìm kiếm
ProfileView Hồ sơ người dùng và cài đặt

ViewModels

@MainActor
final class HomeViewModel: ObservableObject {
    @Published var isLoading: Bool = false
    @Published var items: [HomeItem] = []
    @Published var errorMessage: String?
    
    private let apiService: APIServiceProtocol
    
    func loadData() async { ... }
}

2. Lớp Service

APIService

HTTP client tuân theo nguyên tắc Single Responsibility:

protocol APIServiceProtocol {
    func request<T: Decodable>(
        endpoint: String,
        method: HTTPMethod,
        body: Encodable?,
        headers: [String: String]?
    ) async throws -> T
}

Tính năng:

  • Xử lý request/response generic
  • Tự động mã hóa/giải mã JSON (snake_case ↔ camelCase)
  • Tự động thêm Bearer token
  • Xử lý mã trạng thái HTTP

AuthManager

Singleton quản lý trạng thái xác thực:

final class AuthManager: ObservableObject {
    @MainActor static let shared = AuthManager()
    @Published var authState: AuthState = .unknown
}

Enum AuthState:

enum AuthState {
    case unknown        // Trạng thái khởi tạo
    case unauthenticated // Chưa đăng nhập
    case authenticated(User) // Đã đăng nhập
}

Luồng Dữ Liệu

sequenceDiagram
    participant V as View
    participant VM as ViewModel
    participant S as Service
    participant API as Backend API

    V->>VM: Hành động User (tap button)
    VM->>VM: Đặt isLoading = true
    VM->>S: await service.request()
    S->>API: HTTP Request
    API-->>S: JSON Response
    S-->>VM: Model đã giải mã
    VM->>VM: Cập nhật @Published
    VM-->>V: SwiftUI render lại

Luồng Xác Thực

stateDiagram-v2
    [*] --> Unknown: Khởi động App
    Unknown --> Authenticated: Token hợp lệ
    Unknown --> Unauthenticated: Không có Token
    
    Unauthenticated --> Login
    Login --> Authenticated: Thành công
    Login --> Register: Điều hướng
    Register --> Authenticated: Thành công
    
    Authenticated --> HomeScreen
    HomeScreen --> Unauthenticated: Đăng xuất

Quyết Định Thiết Kế

1. Tại sao MVVM?

Lợi ích Mô tả
Khả năng test ViewModel có thể test độc lập không cần UI
Phân tách trách nhiệm View chỉ hiển thị, logic nằm ở ViewModel
Tương thích SwiftUI @ObservableObject + @Published native
Cập nhật reactive UI tự động làm mới dựa trên Combine

2. Tại sao DI dựa trên Protocol?

// Protocol cho phép mock khi test
protocol APIServiceProtocol { ... }

// Production
final class APIService: APIServiceProtocol { ... }

// Test mock
final class MockAPIService: APIServiceProtocol { ... }

3. Tại sao Keychain thay vì UserDefaults?

Keychain UserDefaults
Mã hóa khi lưu trữ Text thuần
Secure enclave Có thể truy cập
Riêng cho app Shared prefs

4. Tại sao @MainActor trên ViewModels?

  • Đảm bảo tất cả cập nhật @Published xảy ra trên main thread
  • Ngăn chặn vấn đề concurrency với SwiftUI
  • Contract an toàn thread rõ ràng

Kiến Trúc Bảo Mật

┌────────────────────────────────────────────────────────────┐
│                    CÁC LỚP BẢO MẬT                          │
├────────────────────────────────────────────────────────────┤
│  Lớp 1: Bảo mật Transport (HTTPS/TLS)                      │
│  • Tất cả API calls sử dụng HTTPS                          │
├────────────────────────────────────────────────────────────┤
│  Lớp 2: Bảo mật Token (Keychain)                           │
│  • Access token lưu trong Keychain                         │
│  • Refresh token lưu trong Keychain                        │
├────────────────────────────────────────────────────────────┤
│  Lớp 3: Bảo mật Session                                    │
│  • Kiểm tra hết hạn token                                  │
│  • Tự động refresh token                                   │
├────────────────────────────────────────────────────────────┤
│  Lớp 4: Validation Input                                   │
│  • Kiểm tra định dạng email                                │
│  • Kiểm tra độ mạnh mật khẩu                               │
└────────────────────────────────────────────────────────────┘

Hướng Phát Triển

Tính năng Ưu tiên Mô tả
Certificate Pinning Cao Xác thực chứng chỉ TLS
Xác thực sinh trắc Cao Đăng nhập Face ID / Touch ID
Chế độ Offline Trung bình Cache local với SwiftData
Push Notifications Trung bình Tích hợp APNs

Tài Liệu Liên Quan

  • README.md - Hướng dẫn bắt đầu nhanh