Skip to main content

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

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 (AsyncNotifier providers), 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.dart is 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 from lib/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.
Business logic must not live in widgets. If a widget contains a calculation, move it to the feature’s Domain or Application layers.

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