VEU.WathBody/1-docs/4-modules/m03-program-store/module_reqs.md

78 lines
5.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Требования к модулю: 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` — зелёные;
сборка решения без ошибок.