---
title: "Architecture and engineering rules"
url: https://phatdev.net/en/docs/creditmate/kien-truc
lang: en
updated: 2026-10-21
alternate:
  vi: https://phatdev.net/docs/creditmate/kien-truc
markdown: https://phatdev.net/en/docs/creditmate/kien-truc.md
---

# Architecture and engineering rules

Part of the doc set: [CreditMate](https://phatdev.net/en/docs/creditmate.md)

> SwiftUI + SwiftData, the CreditMateKit package, integer money, ppm rates, testing.

**CreditMate** is built with SwiftUI and SwiftData on Swift 6 with strict concurrency checking, targeting iOS and iPadOS 18 or later. The central idea of the architecture is to separate business logic from the interface, so that calculations involving money can be tested independently and repeatedly.

## Code structure

The app contains only the interface and the wiring. All logic lives in a Swift package called `CreditMateKit`, with these targets:

| Target | Role |
|---|---|
| `Money` | Exact money, ratios, the bank calendar, display styles |
| `Domain` | Ledger, card catalog, installments, loans, offers, MCC, analytics, CIC reports |
| `Persistence` | SwiftData models, versioned migrations, encrypted backup |
| `DocumentIntake` | PDF, image and CSV import; statement and CIC readers that produce drafts |
| `CardImage` | On-device masking of card photos |
| `Vault` | The payment-details vault |
| `BankKnowledge` | Card knowledge and offers library |
| `Assistant` | Conversation, tools, memory, budget |
| `AssistantProviders` | Apple on-device, Gemini, Claude, ChatGPT, OpenAI-compatible local AI |

The Xcode project is generated by XcodeGen from `project.yml`. Besides the main app there is a Share Extension that receives shared links and images into "Shared items".

## Rules for money and data

- **Money is an integer.** An amount is a quantity in the smallest unit under ISO 4217 (dong for VND, cent for USD, yen for JPY) plus a currency code. No floating point numbers for money, ratios or exchange rates.
- **Ratios are parts per million (ppm).** 6.8% is stored as 68,000. Multiplication and division use integers with explicit rounding rules and overflow checks.
- **Unknown is not zero.** An unknown value is `nil` and is never replaced with 0 when adding or displaying. Two currencies without a sourced exchange rate are never added together.
- **Dates are LocalDate.** A calendar date is a year, month and day; the default bank time zone is Asia/Ho_Chi_Minh. Time is injected through a `Clock`, so tests do not depend on the device clock. Statement date, due date, purchase date and posting date are four separate concepts.
- **Domain is pure Swift.** `Money` and `Domain` import no SwiftUI, SwiftData or networking.
- **No cascade deletes of history.** A card or account with transactions is archived, not deleted.
- **A limit is stored once** at the shared limit group.

## CloudKit compatibility

SwiftData models are written to be ready for iCloud sync: no uniqueness constraints, every property is either optional or has a default value, relationships are optional, and financial data has no cascade delete. Duplicate prevention happens in the service layer through idempotency keys and external references, with tests. CloudKit is not enabled yet.

## Drafts and posting

Every input source, including the AI assistant, only creates drafts. Posting happens only through an Application Service after the user confirms, with a guard against double posting. This is a project rule, not an arbitrary habit.

## Testing

- **Domain tests** for the package: Money, Domain, Persistence, DocumentIntake, Assistant, Vault and other targets, run with `./scripts/test-kit.sh`. Money calculations are covered by tests written first, based on a set of scenarios with fictional figures.
- **UI tests** run on iPhone and iPad, on iOS 18 and iOS 27. The default test run uses Vietnamese, with an additional smoke test for English.
- There are tests for the balance-recording rules, no double posting on re-import, totals that never turn `nil` into 0, and currencies other than VND.

## Developing with Claude Code

The project is developed with the help of Claude Code. A rule set in the repository's CLAUDE.md file constrains how work is done: rules about money, drafts and security, and a requirement that each important function carries a comment explaining its business meaning. Design decisions are recorded as separate documents. Results must be verified by running real tests, not just by description.
