diff --git a/GO-002-plan.md b/GO-002-plan.md new file mode 100644 index 0000000..2375b45 --- /dev/null +++ b/GO-002-plan.md @@ -0,0 +1,164 @@ +# План GO-002: разорвать зависимости modules от repos и services + +## Цель + +Технические modules не импортируют прикладные `services` и storage `repos`. +Модуль получает собственные входные типы, а преобразование repository-моделей +делается на границе service. + +Не меняем: + +- protobuf/API и сгенерированные файлы; +- формат `scenario.json`, ZIP-архива и публичные ответы; +- бизнес-правила и SQL; +- `internal/modules/storytelling`, пока он не импортирует repos/services; +- GO-006 (`*_utils`) и GO-003 (единый error mapping). + +## Фактические нарушения + +| Module | Нарушение | Callers | +| --- | --- | --- | +| `modules/game_model` | импортирует `repos` и `services/scenarios_service` | game, gameplay, answer, team, MCP, app | +| `modules/scenario_archive` | `Pack` принимает `*repos.Scenario` | `scenarios_service`, module tests | +| `modules/scenario_archive` | импортирует `repos` только для storage-модели | только archive implementation | + +## Правило миграции + +Каждый кусок заканчивается рабочим компилятором и targeted-тестами. После +каждого шага проверять запрещённые импорты: + +```sh +cd evening_detective_server +rg -n '"evening_detective_server/internal/(repos|services)' internal/modules --glob '*.go' --glob '!*_test.go' +go test ./internal/modules/... ./internal/services/... +``` + +## Маленькие этапы + +### GO-002.0 — зафиксировать baseline + +1. Сохранить список runtime-файлов с нарушениями: + `modules/game_model/game.go`, `game_mapper.go`, `team_mapper.go` и + `modules/scenario_archive/interface.go`/`archive.go`. +2. Проверить чистое состояние тестов: + + ```sh + GOCACHE=/tmp/evening-detective-go-cache go test ./internal/modules/... ./internal/services/... + ``` + +3. Не менять callers и не создавать `common`, `domain` или `utils` только для + обхода импортов. + +Готово, если baseline проходит и список зависимостей зафиксирован. + +### GO-002.1 — выделить собственный входной тип archive module + +Файлы: `internal/modules/scenario_archive/interface.go`, `archive.go` и +`archive_test.go`. + +1. Добавить в module небольшой тип входа для упаковки архива, содержащий только + нужные поля: `Name`, `Description`, `Image` и сериализованную `Story`. +2. Изменить `IScenarioArchive.Pack` так, чтобы он принимал этот тип, а не + `*repos.Scenario`. +3. В implementation заменить обращения к `scenario.Scenario`, + `scenario.Description` и другим полям на поля нового типа. +4. Перевести module tests на новый тип. Формат `ScenarioJSON` не менять. + +Проверка: + +```sh +cd evening_detective_server +find internal/modules/scenario_archive -name '*.go' -print0 | xargs -0 gofmt -w +go test ./internal/modules/scenario_archive +! rg 'internal/repos' internal/modules/scenario_archive --glob '*.go' +``` + +Ожидаемый результат: archive module полностью независим от storage package. + +### GO-002.2 — сделать mapping repository → archive в scenarios service + +Файлы: `internal/services/scenarios_service/service.go`, mapper и тесты. + +1. В `scenarios_service` собрать новый archive input из уже полученного + `*repos.Scenario`. +2. Передавать его в `scenarioArchive.Pack`; repository-зависимость остаётся + только в service, где ей место. +3. Не переносить в module права, запросы, storage и protobuf. +4. Сохранить поведение изображений, `nil`-описания и ссылок на story. +5. Добавить/обновить один round-trip тест `DownloadArchive` → `UploadArchive`. + +Проверка: + +```sh +cd evening_detective_server +go test ./internal/services/scenarios_service ./internal/modules/scenario_archive +! rg 'internal/repos' internal/modules/scenario_archive --glob '*.go' +``` + +### GO-002.3 — отделить game model от modules + +Файлы: текущий `internal/modules/game_model/*` и callers. + +1. Переместить типы `Game` и `Team` в пакет с той же предметной ответственностью + под `internal/services/game_model`. +2. Сохранить имена типов и поля, чтобы это была механическая смена import path, + а не новая модель или DTO. +3. Перенести вместе с типами текущие `MapGame`, `MapGames`, `MapTeam`, + `MapTeams`; они являются преобразованием storage-моделей в use-case-модели. +4. Обновить callers в `game_service`, `gameplay_service`, `answer_service`, + `team_service`, `mcp_service`, app и тестах. +5. Не делать пакет `common` и не дублировать структуры. + +Проверка после механического переноса: + +```sh +cd evening_detective_server +find internal/services/game_model internal/services/game_service internal/services/gameplay_service internal/services/answer_service internal/services/team_service internal/services/mcp_service internal/app -name '*.go' -print0 | xargs -0 gofmt -w +go test ./internal/services/game_model ./internal/services/game_service ./internal/services/gameplay_service ./internal/services/answer_service ./internal/services/team_service ./internal/services/mcp_service ./internal/app +! rg 'internal/modules/game_model' --glob '*.go' +``` + +### GO-002.4 — удалить старый module game_model + +1. После успешного переноса убедиться, что `internal/modules/game_model` больше + не импортируется и не нужен через тесты. +2. Удалить только старые файлы package; новые копии не оставлять параллельно. +3. Проверить `go list ./...`, чтобы не осталось пустого/дублирующего пакета. + +Проверка: + +```sh +cd evening_detective_server +go list ./... +go test ./... +! test -d internal/modules/game_model +``` + +### GO-002.5 — финальная проверка направления зависимостей + +1. Runtime modules не импортируют `internal/repos`, `internal/services` или + `internal/app`. +2. `scenario_archive` принимает только собственные типы и callbacks; SQL, + права и storage остаются снаружи. +3. `cmd` по-прежнему собирает concrete repositories/modules/services. +4. Проверить, что сценарный архив и game/team responses побайтно/структурно не + изменились в существующих тестах. +5. Запустить финальный gate: + + ```sh + cd evening_detective_server + find internal -type f -name '*.go' -print0 | xargs -0 gofmt -l + go vet ./... + go test ./... + ``` + +6. Только после успешного полного gate отметить GO-002 как `DONE` в `todo.md`. + +## Критерий готовности GO-002 + +- `rg 'internal/(repos|services)' internal/modules --glob '*.go' --glob '!*_test.go'` не находит runtime-импортов. +- Нет дубликатов `Game`, `Team` или archive input model. +- Все callers используют один пакет `services/game_model`. +- ZIP/`scenario.json`, game/team responses и существующие error semantics не + изменились. +- `gofmt`, `go vet ./...` и `go test ./...` проходят. diff --git a/todo.md b/todo.md index ee89e3f..045ef2a 100644 --- a/todo.md +++ b/todo.md @@ -33,6 +33,12 @@ ### GO-002 — разорвать неправильные зависимости modules от application layers +- Статус: `DONE` (этапы GO-002.1–GO-002.5 выполнены) +- Результат: archive module использует собственный input type, `game_model` + перенесён в `internal/services/game_model`, старые module-зависимости удалены. +- Проверка: runtime modules не импортируют repos/services/app; `gofmt`, + `go vet ./...` и `go test ./...` проходят. + - `internal/modules/game_model` импортирует `internal/repos` и `internal/services/scenarios_service`. - `internal/modules/scenario_archive` импортирует типы `internal/repos`.