78 lines
5.6 KiB
Markdown
78 lines
5.6 KiB
Markdown
# Требования к модулю: m03-program-store
|
||
|
||
> Модуль — сервис `ProgramStore` в `Server.WatchBody.Core`: единая точка
|
||
> работы с программами атлета — версионный JSON, жизненный цикл версий
|
||
> `draft → active → archived`, гарантия «`active` — ровно одна».
|
||
> Самодостаточная копия требований (d-dev-6).
|
||
|
||
## 1. Назначение и границы
|
||
|
||
**В границах модуля:**
|
||
|
||
- класс `ProgramStore` (`Server.WatchBody.Core`, каталог `Programs\`):
|
||
сервис над репозиториями m01 (`ProgramRepository`,
|
||
`ProgramVersionRepository`) — единственный вход для всех потребителей
|
||
(REST API m02/m08, ProgressionRules m05);
|
||
- жизненный цикл версий: создание draft (новая версия на основе
|
||
актуальной), активация, архивация;
|
||
- чтение: активная версия (с body), список версий (без body),
|
||
версия по id (с body);
|
||
- тесты сервиса (интеграционные, docker `watchbody-pg`).
|
||
|
||
**Вне границ модуля:** правила прогрессии и делоада (m05 — создают
|
||
draft-версии **через** ProgramStore), импорт пакета программы (m06 —
|
||
тоже через ProgramStore), редактор программы на телефоне (m09),
|
||
запись версий из sync в m02 (остаётся на репозиториях — уже работает;
|
||
переход на ProgramStore — кандидат рефакторинга, не в v1).
|
||
Схема тела программы (недели → тренировки → упражнения → подходы-план,
|
||
дизайн LLM-планирования разд. 2) в v1 сервером не валидируется —
|
||
хранится как есть (jsonb).
|
||
|
||
## 2. Стек и соглашения
|
||
|
||
- C# / .NET 10, `Server.WatchBody.Core`; доступ к БД — только
|
||
репозитории m01 (Kit.Helpers), прямого SQL нет.
|
||
- Конфиг `Progression.*`/`Deload.*` в модуле не потребляется (это m05).
|
||
- Логи — Kit.Helpers `Log.Logger` (уровень Info).
|
||
|
||
## 3. Инварианты (🛡 — нарушать запрещено)
|
||
|
||
1. **Программы принадлежат атлету** — все операции принимают
|
||
`athleteId`; чужие программы/версии недоступны.
|
||
2. **Неизменяемость версии** — после создания `body`, `author`,
|
||
`based_on_version` не меняются; меняется только `status`.
|
||
3. **Ровно одна `active` на программу** — активация переводит прежнюю
|
||
активную в `archived` (гарантия БД — частичный уникальный индекс;
|
||
ProgramStore использует только переходы, разрешённые БД).
|
||
4. **Переходы статусов только `draft → active → archived`** — повторная
|
||
активация archived-версии запрещена (явное действие пользователя —
|
||
Ф-10.4); 🛡 автоматической смены активной версии нет (vision 2.4).
|
||
5. **Тренировки ссылаются на версию, активную на их начало** —
|
||
ProgramStore не перепривязывает ссылки (история не «поплывёт»).
|
||
|
||
## 4. Состав сервиса (контракты, шаг 1)
|
||
|
||
| # | Метод | Назначение |
|
||
|---|---|---|
|
||
| 1 | `EnsureProgram(athleteId, title)` | программа атлета (создаёт при отсутствии; UNIQUE(athlete_id)) |
|
||
| 2 | `CreateDraft(athleteId, body, author, rationale, basedOnVersion = null)` | новая draft-версия: `version = max+1` (БД), `based_on_version` = актуальная максимальная (или явная); author ∈ user/admin/rules (llm — v2) |
|
||
| 3 | `Activate(athleteId, versionId)` | draft → active; прежняя active → archived; не-draft → ошибка |
|
||
| 4 | `Archive(athleteId, versionId)` | active → archived (смена активной = активация другой версии) |
|
||
| 5 | `GetActive(athleteId)` | активная версия с body (или null) |
|
||
| 6 | `ListVersions(athleteId)` | версии программы без body (статусы, номера, rationale) |
|
||
| 7 | `GetVersion(athleteId, versionId)` | версия с body |
|
||
|
||
## 5. Критерии приёмки модуля
|
||
|
||
1. Жизненный цикл: CreateDraft → Activate → (новая draft) → Activate —
|
||
активна ровно одна, прежняя archived; повторная активация
|
||
archived — ошибка.
|
||
2. Изоляция: операции одного атлета не видят/не трогают программы
|
||
другого.
|
||
3. Неизменяемость: после создания body/author/based_on_version не
|
||
меняются ни одним методом сервиса.
|
||
4. CreateDraft фиксирует `based_on_version` = актуальная максимальная
|
||
версия (история версий — цепочка).
|
||
5. Тесты интеграционные против docker `watchbody-pg` — зелёные;
|
||
сборка решения без ошибок.
|