The Persistence layer stores application data, reconstructs Domain and Application models, and isolates the rest of the project from SQLite-specific details. It records state produced by the Domain; it does not implement learning rules.
flowchart TD
APP["Application"] --> REPOSITORIES["Repositories"]
REPOSITORIES --> DAOS["DAOs"]
REPOSITORIES --> MAPPERS["Mappers"]
DAOS --> DB[(SQLite)]
MAPPERS <--> MODELS["Domain and Application models"]
Repositories expose operations aligned with application use cases. They coordinate DAOs and mappers, rebuild complete objects, and define transaction boundaries that span several tables.
DAOs contain SQL and operate on persistence models. Simple operations open
their own sqlite_async transaction; aggregate writes may receive a transaction
created by a repository.
Persistence models represent normalized stored data. They contain database identifiers, scalar values, timestamps, and enum codes without business behaviour. They never leave the Persistence layer.
Mappers translate persistence models into Domain objects or Application content models and back. They handle structural conversion, not learning decisions.
The database component owns connection lifecycle, native connection
configuration, schema migrations, and access to the shared
sqlite_async.SqliteDatabase instance.
See SQLite database.
For a read, a repository asks one or more DAOs for normalized data, uses mappers to reconstruct the required object, and returns only that object to the Application layer.
For a write, the Domain has already produced the new state. The repository maps it to persistence models and coordinates the necessary DAO operations.
The most important aggregate write occurs after an answer. One transaction stores the exercise progression, sentence progression when applicable, answer history, session result, and resumable exercise snapshots. This prevents the stored exercise and active session from describing different answers.
Durations and review lookup values are stored as integer microseconds.
Date-time values are serialized as ISO-8601 UTC strings and converted back to
local DateTime values when reconstructed. Repository date ranges use an
inclusive start and exclusive end.
The current backend uses sqlite_async native connections and does not support
the Flutter web target.
infrastructure/persistence/
├── database/
├── dao/
├── mappers/
├── models/
└── repositories/