Overview
Canthus is built around a small number of architectural rules:- local-first persistence
- deterministic mana computation
- business logic outside widgets
- domain contracts as the stable seam between UI and storage
Layer model
The codebase is organized around four conceptual layers.Domain has no outward dependencies. It defines interfaces that other layers implement - it never imports from UI, Data, or Platform.
Domain
App-specific business logic.- Mana calculation and pool/cost rules
- Baseline and check-in processing
- Task actionability logic
- Crash cycle (PEM event) detection
- Repository and service interfaces (contracts other layers fulfil)
Data
Local-first data layer. All reads and writes go here first - the app never waits on the network.- Drift/SQLite: encrypted database for user data (tasks, check-ins, events)
shared_preferences: lightweight key-value store for settings and feature flags- Implementations of domain repository interfaces
Platform
Integration with hardware and external services- Push and local notifications
Notifications
Use theflutter_local_notifications package.
pub.dev
UI
Flutter widgets, screens, and navigation.No business logic.
- Screens and widgets
- Navigation/routing
- Design system consumption (
lib/design/) - State management (calls Domain services, reads from Data)
Dependency rules
| From | May import | Must not import |
|---|---|---|
| UI | Domain, Platform | Data internals, SQL/Drift details |
| Data | Domain | UI, Platform |
| Platform | Domain | UI, Data |
| Domain | - | UI, Data, Platform |
Data strategy
Local-first
The app is fully functional with no internet connection. SQLite (Drift) is the source of truth for user data. Every feature must work against the local store alone.Storage split
| Store | Contents | Encrypted | Wiped on reset |
|---|---|---|---|
| SQLite (Drift) | Tasks, check-ins, events | Yes | Yes |
shared_preferences | Settings and small local preferences | No | No |