---
title: "Kiến trúc & quy tắc kỹ thuật"
url: https://phatdev.net/docs/moneyflow/kien-truc
lang: vi
updated: 2026-10-23
alternate:
  en: https://phatdev.net/en/docs/moneyflow/kien-truc
markdown: https://phatdev.net/docs/moneyflow/kien-truc.md
---

# Kiến trúc & quy tắc kỹ thuật

Thuộc bộ tài liệu: [MoneyFlow](https://phatdev.net/docs/moneyflow.md)

> SwiftUI + SwiftData, máy chủ Node.js, quy tắc đồng bộ, kiểm tra dữ liệu, kiểm thử.

**MoneyFlow** gồm một ứng dụng iOS/iPadOS viết bằng SwiftUI và SwiftData (Swift 5, nhắm tới iOS và iPadOS 26.1 trở lên) và một máy chủ đồng bộ Node.js + Express + PostgreSQL. Trọng tâm kiến trúc là làm cho sổ chung của nhiều người luôn nhất quán, kể cả khi có thiết bị ngoại tuyến hay lệch giờ.

## Cấu trúc mã nguồn

```
MoneyFlow (ứng dụng iOS / iPadOS)
├── Views       Giao diện SwiftUI: Tổng quan, Giao dịch, Tiết kiệm, Danh mục, Tài khoản...
├── Managers    DataManager, SavingsManager, UserManager, AppLockManager, NotificationManager (@Observable)
├── Models      Model SwiftData: Transaction, Category, Budget, Gold, Savings, SavingsGoal, DebtRecord...
├── Services    SyncManager (đồng bộ push/pull), GoldPriceService (giá vàng)
├── Helpers     CategorySuggester, DataExporter (CSV), ImageStorageHelper, DemoData
└── Theme.swift Hệ thống thiết kế: màu, kiểu chữ, thẻ gradient, Liquid Glass

Backend (Node.js + Express + PostgreSQL)
├── routes      auth, sync, gold
├── middleware  xác thực JWT
└── db          khởi tạo lược đồ và kết nối
```

| Phần | Vai trò |
|---|---|
| `Views` | Năm tab: Tổng quan, Giao dịch, Thêm (mở màn nhập), Tiết kiệm, Danh mục. Ngân sách, Báo cáo, Định kỳ, Hoá đơn, Nợ, Chia tiền nằm trong Tài khoản. |
| `Managers` | Lớp quản lý dữ liệu dùng macro `@Observable`: `DataManager` (giao dịch, danh mục, ngân sách, định kỳ, hoá đơn, nợ, chia tiền), `SavingsManager` (vàng, tiết kiệm, mục tiêu), `UserManager`, `AppLockManager`, `NotificationManager`. |
| `Models` | Mười model SwiftData: Transaction, Category, Budget, User, Gold, Savings, RecurringTransaction, BillReminder, DebtRecord, SavingsGoal. |
| `Services` | `SyncManager` đồng bộ với máy chủ; `GoldPriceService` lấy giá vàng. |
| `Helpers` | `CategorySuggester` (gợi ý danh mục), `DataExporter` (CSV), `ImageStorageHelper` (ảnh hoá đơn), `DemoData` (chỉ bản Debug). |
| `Theme.swift` | Màu thích ứng sáng/tối, kiểu chữ theo Dynamic Type, chữ số dạng bảng cho số tiền, bốn kiểu thẻ gradient (xanh ngọc, hồng cam, vàng, tím), Liquid Glass cho thanh điều hướng có phương án dự phòng. |

Máy chủ có các đường dẫn chính: `POST /api/auth/login`, `GET /api/sync/pull`, `POST /api/sync/push`, `POST /api/sync/reset` và `GET /api/gold/price`.

## Quy tắc đồng bộ

- **Danh mục mặc định có mã định danh tất định** theo tên và loại, để mọi thiết bị khớp nhau mà không cần hỏi máy chủ. Đổi tên một danh mục mặc định phải giữ mã cũ qua `idSeed` (ví dụ "Xe cộ" đổi thành "Độ xe" mà vẫn giữ mã cũ).
- **Xoá là xoá mềm** (`isDeleted`) để thay đổi được đồng bộ sang thiết bị khác.
- **Con trỏ đồng bộ dùng thời gian của máy chủ.** Mỗi dòng trên máy chủ có hai mốc: `updated_at` do thiết bị gửi, chỉ dùng để phân xử "bản sửa sau cùng thắng", và `server_updated_at` do cơ sở dữ liệu gán, là con trỏ kéo. Nhờ đó chỉnh sửa gửi muộn từ máy lệch giờ vẫn đến được các máy khác.
- **Kéo không bỏ sót.** Đẩy dữ liệu giữ một khoá tư vấn dùng chung trong giao dịch của nó, còn kéo dữ liệu lấy khoá độc quyền vừa đủ để mở ảnh chụp và đọc đồng hồ máy chủ. Vì vậy khi con trỏ được chốt, mọi lần đẩy đang chạy đã hoàn tất.
- **Kiểm tra dữ liệu gửi lên** bằng bộ kiểm tra riêng (`validatePushBody`) với giới hạn độ dài cột, số bản ghi mỗi bảng (5.000), số nhãn, năm hợp lệ, và `updated_at` không được vượt thời gian hiện tại quá 5 phút.
- **Đẩy theo lô 500 bản ghi.** Nếu một lô thất bại thì chưa ghi nhận tiến triển nào, lần đồng bộ sau gửi lại.

## Quy tắc về dữ liệu và bảo mật

- Không đưa mật khẩu, khoá API hay dữ liệu tài chính thật vào mã nguồn; cấu hình nhạy cảm nằm trong `.env`, đã được loại khỏi git.
- Số tiền tính theo đồng (VND), hiển thị theo chuẩn Việt Nam: `1.234.567 đ`, dạng rút gọn `51,9tr`. Một giao dịch nhập tay tối đa 100 tỷ đồng.
- Dữ liệu demo nằm trong kho riêng, đồng bộ bị tắt trong chế độ demo.
- Khi lược đồ không mở được, kho cũ được đổi tên rồi giữ lại thay vì xoá.
- Thông báo hoá đơn được lên lịch lại từ đầu mỗi lần mở ứng dụng, và dọn các thông báo của hoá đơn không còn tồn tại.

## Kiểm thử

- **63 test ứng dụng** (`MoneyFlowTests`) chạy trên kho SwiftData trong bộ nhớ với lược đồ đầy đủ. Phạm vi gồm hoá đơn (chu kỳ, quá hạn), giao dịch định kỳ (ngày gốc, tạm dừng), ngân sách cuốn chiếu, phân tích giá vàng và trạng thái giá cũ, định dạng tiền, xuất CSV, màn nhập giao dịch và bộ gợi ý danh mục.
- **Kiểm thử máy chủ** cho phần kiểm tra dữ liệu đồng bộ: `npm test` chạy `node test/validate.test.js`.
- Chạy test ứng dụng bằng Xcode chính thức; bản beta có thể làm trình chạy kiểm thử không phản hồi.

```sh
xcodebuild test -project MoneyFlow.xcodeproj -scheme MoneyFlow \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \
  -only-testing:MoneyFlowTests
```

## Người đóng góp

- **Steven Lam**: tác giả. Lên kế hoạch và định hướng sản phẩm, thiết kế kiến trúc, định hướng thiết kế, vận hành máy chủ, kiểm thử thực tế và phát hành.
- **Claude** (Anthropic): viết code theo kế hoạch và kiến trúc của tác giả, qua Claude Code. Các commit viết cùng Claude có dòng `Co-Authored-By: Claude`.
- **Gemini** (Google): chỉnh giao diện, qua Antigravity.
- **Codex** (OpenAI): review code, góp ý về lỗi.

Mọi thay đổi được kiểm chứng bằng cách chạy kiểm thử thật, không chỉ dựa vào mô tả.
