Local-First Data Flow
For this stack, local-first means the app remains useful when the network is absent and user data lives in a local source of truth.
flowchart LR
input["User input or SMS import"] --> repo["Repository"]
repo --> dao["Room DAO"]
dao --> db[("SQLite via Room")]
db --> flow["Flow query"]
flow --> vm["ViewModel UI state"]
vm --> ui["Compose list"]
repo --> export["CSV or backup export"]
Default Rule
Room is the source of truth for structured app data.
Use this flow:
DAO exposes Flow -> repository maps domain rows -> ViewModel exposes UI state -> Compose renders rows
Writes should also go through repositories:
UI event -> ViewModel -> repository -> DAO transaction
SMS scan -> repository -> parser -> DAO transaction
Worker -> repository -> DAO read/write -> export or sync
Finance App Shape
For a ledger or expense tracker, keep these concepts separate:
| Concept | Example | Storage Rule |
|---|---|---|
| Raw import | Original SMS body, sender, timestamp | Store only if needed and disclosed |
| Parsed transaction | amount, direction, merchant, account, date | Store as normalized Room entity |
| User correction | renamed merchant, category override | Store separately or as explicit fields |
| Export row | CSV-safe data | Generate from normalized data, not raw SMS by default |
Migrations
Treat each schema change as a user-data migration.
Required practices:
- Export Room schemas.
- Write migration tests before release.
- Avoid destructive migration for real user data.
- Back up or export before high-risk migrations.
- Keep version changes small and explainable.
Backup And Export
Start with explicit user-initiated export. It is simpler and easier to explain than silent cloud sync.
Good v1 export options:
- CSV for spreadsheet compatibility.
- JSON for lossless app backup.
- ZIP bundle only when there are multiple files.
Avoid automatic cloud backup for sensitive finance data until privacy policy, encryption, deletion, and account recovery are designed.
Encryption Decision
Encryption is valuable, but it adds key management and recovery risk. For a first local-only prototype, document whether data is plain Room storage, encrypted Room storage, or export-only encrypted.
Do not imply encryption unless the app actually implements and tests it.
Gotcha: Privacy-first does not mean “no policy work.” If you read SMS, you still need clear disclosure, data minimization, restricted permission declaration, and a useful denied-permission mode.