8.1 KiB
План 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-тестами. После каждого шага проверять запрещённые импорты:
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
-
Сохранить список runtime-файлов с нарушениями:
modules/game_model/game.go,game_mapper.go,team_mapper.goиmodules/scenario_archive/interface.go/archive.go. -
Проверить чистое состояние тестов:
GOCACHE=/tmp/evening-detective-go-cache go test ./internal/modules/... ./internal/services/... -
Не менять callers и не создавать
common,domainилиutilsтолько для обхода импортов.
Готово, если baseline проходит и список зависимостей зафиксирован.
GO-002.1 — выделить собственный входной тип archive module
Файлы: internal/modules/scenario_archive/interface.go, archive.go и
archive_test.go.
- Добавить в module небольшой тип входа для упаковки архива, содержащий только
нужные поля:
Name,Description,Imageи сериализованнуюStory. - Изменить
IScenarioArchive.Packтак, чтобы он принимал этот тип, а не*repos.Scenario. - В implementation заменить обращения к
scenario.Scenario,scenario.Descriptionи другим полям на поля нового типа. - Перевести module tests на новый тип. Формат
ScenarioJSONне менять.
Проверка:
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 и тесты.
- В
scenarios_serviceсобрать новый archive input из уже полученного*repos.Scenario. - Передавать его в
scenarioArchive.Pack; repository-зависимость остаётся только в service, где ей место. - Не переносить в module права, запросы, storage и protobuf.
- Сохранить поведение изображений,
nil-описания и ссылок на story. - Добавить/обновить один round-trip тест
DownloadArchive→UploadArchive.
Проверка:
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.
- Переместить типы
GameиTeamв пакет с той же предметной ответственностью подinternal/services/game_model. - Сохранить имена типов и поля, чтобы это была механическая смена import path, а не новая модель или DTO.
- Перенести вместе с типами текущие
MapGame,MapGames,MapTeam,MapTeams; они являются преобразованием storage-моделей в use-case-модели. - Обновить callers в
game_service,gameplay_service,answer_service,team_service,mcp_service, app и тестах. - Не делать пакет
commonи не дублировать структуры.
Проверка после механического переноса:
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
- После успешного переноса убедиться, что
internal/modules/game_modelбольше не импортируется и не нужен через тесты. - Удалить только старые файлы package; новые копии не оставлять параллельно.
- Проверить
go list ./..., чтобы не осталось пустого/дублирующего пакета.
Проверка:
cd evening_detective_server
go list ./...
go test ./...
! test -d internal/modules/game_model
GO-002.5 — финальная проверка направления зависимостей
-
Runtime modules не импортируют
internal/repos,internal/servicesилиinternal/app. -
scenario_archiveпринимает только собственные типы и callbacks; SQL, права и storage остаются снаружи. -
cmdпо-прежнему собирает concrete repositories/modules/services. -
Проверить, что сценарный архив и game/team responses побайтно/структурно не изменились в существующих тестах.
-
Запустить финальный gate:
cd evening_detective_server find internal -type f -name '*.go' -print0 | xargs -0 gofmt -l go vet ./... go test ./... -
Только после успешного полного 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 ./...проходят.