State Management with Riverpod
The app uses Riverpod with an explicit action layer:UI -> ViewModel -> Application Action -> Repository -> Drift
For the full app directory map and layer walkthrough, see App architecture.
Architecture
The Flutter app follows layered boundaries where each layer depends only on the next layer down.- UI: widgets/screens + feature ViewModels
- Domain application: one class per operation (actions)
- Domain repositories: abstract contracts
- Data repositories: Drift-backed implementations
Application Actions
Application actions are the boundary between ViewModels and repositories. Rules:- One class per operation
- Verb-phrase class names
- No
UseCasesuffix call(...)API with named parameters- All actions include logging
- Futures return
ApplicationResult<T>(Success<T>/Failure<T>)
- Mutation actions log
infoon start/success - Read and compute actions log
debugon start/success - Watch actions log
debugwhen a subscription starts and when it terminates - Validation / not-found / conflict failures log
warning - Unexpected / infrastructure failures log
error - Logs must include metadata only: IDs, counts, date windows, and filter presence
- Logs must not include task titles, raw event payloads, or raw check-in
body/mind/moodvalues
CreateTaskSaveCheckInGetEventsInWindow
Repository Contracts
Repository write contracts use named parameters directly.- No write DTO classes in repository interfaces
- Field-level partial updates use
FieldUpdate<T> - Repository adapters keep persistence/event semantics
Provider Wiring
Core providers live underlib/ui/core/providers/.
repository_providers.dart: repository instanceslogger_provider.dart: shared loggertask_actions_providers.dartcheck_in_actions_providers.dartevent_actions_providers.dartmana_actions_providers.dart
taskListProvider) also live in ui/core/providers and must call read actions, not repositories directly.
ViewModel Conventions
ViewModels remainAsyncNotifier<void> in this phase.
- Commands are methods on the ViewModel
- Commands return
Future<ApplicationResult<T>> - ViewModels read action providers, not repository providers
Folder Layout
Naming
| Thing | Convention | Example |
|---|---|---|
| Application action | Verb phrase, no suffix | CreateTask, GetTaskById |
| Repository interface | {Entity}Repository | TaskRepository |
| Drift implementation | Drift prefix | DriftTaskRepository |
| ViewModel | {Feature}ViewModel | TasksViewModel |
| ViewModel provider | {feature}ViewModelProvider | tasksViewModelProvider |
Async Patterns
- Reactive reads:
StreamProvider+ watch actions - Point-in-time reads/writes:
Future+ApplicationResult<T> - No raw Drift query exposure above data adapters