Skip to main content

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
At the app level, the main execution path is documented in App architecture.

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 the flutter_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

FromMay importMust not import
UIDomain, PlatformData internals, SQL/Drift details
DataDomainUI, Platform
PlatformDomainUI, Data
Domain-UI, Data, Platform
Layer violations are prohibited and must be caught in code review. Domain is the dependency target, never the dependency source. Business logic must not live in widgets. If a widget contains a calculation, move it to Domain.

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

StoreContentsEncryptedWiped on reset
SQLite (Drift)Tasks, check-ins, eventsYesYes
shared_preferencesSettings and small local preferencesNoNo
The split ensures a nuclear data reset clears all user data while preserving app preferences.

App-specific references


References