Architecture
Salli's presentation, application, domain, data, and native Android boundaries.
Salli keeps financial behavior on the device and separates deterministic domain logic from storage, native capabilities, and presentation.
flowchart TD
UI["Presentation\nFlutter screens and widgets"] --> APP["Application\nRiverpod state + orchestration"]
APP --> DOMAIN["Domain\nEntities and deterministic use cases"]
APP --> STORE["Store interface"]
STORE --> DB["Drift + SQLCipher\nschema 28"]
APP --> SMS["SMS ingestion and parser"]
APP --> OCR["Receipt OCR and extractor"]
APP --> REM["Local reminder planner"]
ANDROID["Android Kotlin\nreceiver, worker, widget"] --> SMS
SMS --> DOMAIN
OCR --> DOMAIN
DOMAIN --> APP
Core
Money wraps integer minor units. Wallet-currency helpers preserve native
values and explicit LKR conversion evidence. Formatting occurs at the UI edge.
Domain
Pure Dart entities and use cases implement:
- transactions and analytics;
- Safe to Spend and payday cycles;
- budget rollover and insights;
- seettu schedules;
- reminder planning;
- runway forecasts; and
- local intelligence records.
Domain code does not depend on Flutter widgets or SQLite.
Data
- Drift tables and migrations.
- SQLCipher device-store factory.
- JSON personal backup and CSV export.
- Android SMS gateway, parser, classifier, deduplication, and evidence queues.
- Local merchant corpus and package validation.
- Receipt image/OCR adapters.
- Local notification adapters.
- Reference exchange-rate adapter with minimized request fields.
Application
Riverpod exposes one hydrated AppState and intent-style notifier methods.
Writes are serialized through the store. Optimistic state updates keep screens
reactive, while persistence tests verify restart hydration.
Presentation
The shell uses an indexed stack for Home, Activity, Insights, and More.
Standard Navigator routes open full screens; modal sheets handle focused
editing and review. The Ledger theme supplies colors, typography, radii,
spacing, and Android's classic variant.
Native Android
Kotlin components handle:
SMS_RECEIVED;- permission and inbox backfill channels;
- background one-shot processing;
- sender policy;
- notification delivery;
- reboot restoration; and
- the home-screen widget.
Native code passes bounded data to Dart rather than duplicating financial logic.
No production backend
Cloud sync, family sharing, billing, remote AI, and a signed corpus/template service are deferred. The current app has no backend dependency.