CreditMate được xây bằng SwiftUI và SwiftData trên Swift 6 với kiểm tra đồng thời nghiêm ngặt, nhắm tới iOS và iPadOS 18 trở lên. Trọng tâm kiến trúc là tách logic nghiệp vụ khỏi giao diện, để những phép tính liên quan đến tiền có thể kiểm thử độc lập và lặp lại.
Cấu trúc mã nguồn
Ứng dụng chỉ chứa giao diện và phần nối. Toàn bộ logic nằm trong một Swift package tên CreditMateKit, gồm các target:
| Target | Vai trò |
|---|---|
Money |
Tiền chính xác, tỷ lệ, lịch ngân hàng, kiểu hiển thị |
Domain |
Sổ giao dịch, danh mục thẻ, trả góp, khoản vay, ưu đãi, MCC, phân tích, báo cáo CIC |
Persistence |
Model SwiftData, migration theo phiên bản, sao lưu mã hoá |
DocumentIntake |
Nhập PDF, ảnh, CSV; bộ đọc sao kê và báo cáo CIC thành bản nháp |
CardImage |
Che thông tin trên ảnh chụp thẻ ngay trên máy |
Vault |
Két thông tin thanh toán |
BankKnowledge |
Kho kiến thức thẻ và ưu đãi |
Assistant |
Hội thoại, công cụ, bộ nhớ, ngân sách |
AssistantProviders |
Apple on-device, Gemini, Claude, ChatGPT, AI cục bộ tương thích OpenAI |
Dự án Xcode được sinh bằng XcodeGen từ project.yml. Ngoài ứng dụng chính còn có Share Extension, nhận liên kết và ảnh chia sẻ vào mục "Mục đã chia sẻ".
Quy tắc về tiền và dữ liệu
- Tiền là số nguyên. Một khoản tiền gồm số lượng ở đơn vị nhỏ nhất theo ISO 4217 (đồng cho VND, cent cho USD, yên cho JPY) và mã tiền tệ. Không dùng số thực cho tiền, tỷ lệ hay tỷ giá.
- Tỷ lệ là phần triệu (ppm). 6,8% lưu là 68.000. Phép nhân chia dùng số nguyên với quy tắc làm tròn tường minh và kiểm tra tràn số.
- Chưa biết khác 0. Giá trị chưa biết là
nilvà không bao giờ bị thay bằng 0 khi cộng hay hiển thị. Hai loại tiền chưa quy đổi có nguồn không được cộng với nhau. - Ngày là LocalDate. Ngày lịch gồm năm, tháng, ngày; múi giờ ngân hàng mặc định là Asia/Ho_Chi_Minh. Thời gian được tiêm qua
Clock, nên kiểm thử không phụ thuộc đồng hồ máy. Ngày sao kê, ngày đến hạn, ngày mua và ngày hạch toán là bốn khái niệm khác nhau. - Domain là Swift thuần. Không import SwiftUI, SwiftData hay mạng trong
MoneyvàDomain. - Không xoá dây chuyền lịch sử. Thẻ và tài khoản có giao dịch thì lưu trữ, không xoá.
- Hạn mức lưu một lần ở nhóm hạn mức dùng chung.
Tương thích CloudKit
Model SwiftData được viết để sẵn sàng cho đồng bộ iCloud: không dùng ràng buộc duy nhất, mọi thuộc tính hoặc là tuỳ chọn hoặc có giá trị mặc định, quan hệ là tuỳ chọn và không có xoá dây chuyền với dữ liệu tài chính. Chống trùng thực hiện ở tầng dịch vụ bằng khoá idempotency và tham chiếu ngoài, có kiểm thử đi kèm. Hiện chưa bật CloudKit.
Bản nháp và ghi sổ
Mọi nguồn nhập, kể cả trợ lý AI, chỉ tạo bản nháp. Ghi sổ chỉ qua Application Service sau khi người dùng xác nhận, với khoá chống ghi đôi. Điều này nằm trong luật của dự án, không phải thói quen tuỳ ý.
Kiểm thử
- Kiểm thử domain cho package: Money, Domain, Persistence, DocumentIntake, Assistant, Vault và các target khác, chạy bằng
./scripts/test-kit.sh. Các phép tính tiền được viết kiểm thử trước, dựa trên bộ tình huống có số liệu giả. - Kiểm thử giao diện chạy trên iPhone và iPad, với iOS 18 và iOS 27. Bản kiểm thử mặc định chạy tiếng Việt, và có thêm kiểm thử khói cho tiếng Anh.
- Có kiểm thử cho quy tắc ghi dư nợ, chống ghi đôi khi nhập lại, tổng không biến
nilthành 0, và tiền tệ khác VND.
Phát triển cùng Claude Code
Dự án được phát triển với sự hỗ trợ của Claude Code. Bộ luật trong tệp CLAUDE.md của kho mã ràng buộc cách làm: luật về tiền, bản nháp, bảo mật, và yêu cầu mỗi hàm quan trọng có chú thích giải thích ý nghĩa nghiệp vụ. Các quyết định thiết kế được ghi lại thành tài liệu riêng. Kết quả phả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ả.