Domain model and repository layout¶
What Dinatos stores (users, exercises, routines, activities, …) and where the code lives. Read this first if you are new to the codebase: the backend and frontend pages use these terms without redefining them.
For the same model from a user’s point of view, see Concepts.
Concepts¶
Each concept is a database table (or a small group of them) in
backend/src/dinatos_backend/models/.
Term |
Meaning |
|---|---|
User |
A local account. Dinatos is a household app: several people share one server. Everything is owned by exactly one user, except exercises. |
Exercise |
One movement, e.g. “Romanian deadlift”. The only global resource: all users share one catalog. |
Routine |
A saved, named template: an ordered list of exercises, each with planned sets. |
Activity |
A recorded gym session: the exercises actually done, with sets, reps and weights. It comes from running a routine or from a workout built on the spot. Activities feed all stats. |
Planned workout |
A routine (or free-form session) scheduled for a future time, with a duration and a reminder lead time. One calendar entry. |
Superset |
Exercises done back-to-back. Marked by giving consecutive exercises the same |
Set |
One row of weight, reps, distance and duration inside a routine or activity exercise. It has a |
Exercises: built-in vs custom¶
Exercise.is_custom=Falsemarks the shipped catalog. It is seeded on a new instance and is immutable:PATCHandDELETEreturn 403. This stops the catalog that routines and tutorials reference from changing underneath them.is_custom=True(the default) marks an exercise a user created. It can be edited and deleted.
Planned workouts are not activities¶
A plan must not count in stats until it has been performed. When a workout
started from a plan is saved, the plan’s completed_activity_id is set to
the new activity.
Planned workouts appear on Home’s calendar, trigger reminders on Android, and are published as an ICS feed (see Calendar feed).
Tables at a glance¶
Table |
Purpose |
|---|---|
|
Accounts; one settings row per user |
|
Logins and API keys (see Authentication) |
|
The shared catalog |
|
Templates |
|
Performed sessions |
|
Calendar entries |
|
Dated weigh-ins and tape measurements |
|
Duplicate tracking for imports |
Adding a table has a required extra step: register it for backup (see Backup and data export).
Repository layout¶
dinatos/
backend/ FastAPI + async SQLAlchemy + pydantic API, backed by Postgres
frontend/ Flutter app (web and Android; Linux desktop packages too)
mcp_server/ Small stdio proxy to the backend's MCP endpoint
e2e/ Browser tests (Playwright) against the real web build
docs/ This site (Markdown, rendered with Sphinx + MyST)
packaging/ Linux .deb / AppImage packaging
The root Dockerfile and docker-compose.yml build and run everything
together.
Each package directory has its own self-sufficient justfile
(cd backend && just test). The root justfile imports each one as a just
module, so just backend test works from anywhere in the repo.
Backend code is layered under backend/src/dinatos_backend/:
Directory |
Holds |
|---|---|
|
SQLAlchemy tables |
|
pydantic request/response shapes |
|
Business logic |
|
FastAPI routers ( |
Module boundaries¶
backend/tach.toml declares which layers may import which, and just check
(and CI) enforces it. The flow is one-way:
config / models -> schemas -> db -> services -> api -> app (main.py)
If your change needs a new arrow, rethink the change first.