Overview
Canthus is built around a small number of architectural rules:- Local-first persistence: core workflows must work offline (SQLite/Drift).
- Deterministic mana computation: same inputs must produce the same domain outputs.
- Business logic outside widgets: widgets render state and dispatch intents.
- Feature boundaries (FSA): codebase is organized into independent feature slices (
lib/features/) and shared infrastructure (lib/core/).
Code Structure: Core & Features
The codebase implements a Feature-Sliced Architecture (FSA) to group files by product concern rather than layer types.Core (lib/core/)
Shared app infrastructure and platform adapters.
- bootstrap: startup initialization, dependency overrides, and app-lifetime Riverpod providers.
- design: custom design system tokens, typography scales, colors, and reusable widget primitives (
AppButton,AppCard). - persistence: Drift database initialization, encrypted SQLite setup, and shared schema migrations.
- diagnostics: Privacy-safe logging contracts and adapters (
AppDiagnostics). - routing: Navigation shells and route configs.
Features (lib/features/)
Self-contained slices representing distinct product concerns (e.g. home, mana, tasks, check_ins, orchard, insights, onboarding, settings).
Inside each feature slice, code is organized conceptually:
- domain: Local models, rules, and repository contracts. No Flutter, Drift, or Riverpod imports.
- presentation: ViewModels (
AsyncNotifierproviders), local widgets, and screens. - application: Single-purpose command actions (e.g.,
CreateTask,CompleteTask) that orchestrate domain logic and persistence.
Dependency & Import Rules
To keep the monorepo modular and clean, these boundaries are programmatically checked:1. Feature Import Isolation
Features may only import from other features via their public barrel file:package:canthus/features/X/X.dartis allowed.- Importing feature internals (e.g.,
package:canthus/features/X/presentation/widgets/...) is forbidden and will fail the architecture test.
2. Core to Feature Boundary
- Shared components in
lib/core/are accessible to all features. - Code in
lib/core/must never import anything fromlib/features/.
3. Layer Separation
- Domain is the dependency target. It defines interfaces (repository contracts) and must not import from persistence (Drift) or UI (Flutter/Riverpod) layers.
- UI / Presentation views state and triggers events. Feature presentation code must not import Drift database implementation files.
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 |