# План GO-001: убрать прямые зависимости handler-ов от repos и modules ## Цель `internal/app` принимает transport-вход, извлекает пользователя и параметры, вызывает прикладной service и преобразует результат в protobuf/HTTP. Handler не импортирует repository и не владеет прикладной операцией. Не трогаем в этой задаче: - `proto/`, API-контракт и protobuf-генерацию; - wiring в `cmd`, где repositories и modules должны собираться; - `roles` и `processor_jwt`, если они используются только для извлечения identity/проверки transport-доступа; - единое исправление `err.Error()` во всех handler-ах — это GO-003. ## Текущие точки нарушения | Участок | Прямые зависимости app | Что оставить в app | Что должно уйти в service | | --- | --- | --- | --- | | `app/team_registration.go` | `teams_repo` | HTTP/JSON, auth token, timeout | смысл ошибок регистрации | | `app/team_qr.go` | `games_repo`, `team_qr_pdf` | auth, headers, `HttpBody` | ошибки игры и PDF | | `app/users.go` | `users_repo` | claims, metadata, transport auth | ошибка отсутствующего пользователя | | `app/scenario_archive.go` | `scenarios_repo`, archive/string modules | auth, ZIP headers, `HttpBody` | лимиты/ошибки сценарного архива | | `app/files.go` | `file_storage` | auth, headers, `HttpBody` | ошибка отсутствующего файла | | mapper-ы app | `game_model`, `formatter_utils` | protobuf mapping | модели и преобразования use-case | ## Маленькие этапы ### GO-001.0 — зафиксировать baseline и границы 1. Проверить текущий список импортов: ```sh rg -n 'internal/(repos|modules)' evening_detective_server/internal/app --glob '*.go' cd evening_detective_server && go test ./internal/app ./internal/services/... ``` 2. Выписать в diff только файлы из таблицы выше. 3. После каждого следующего этапа проверять, что импорт repository исчез только из app, а wiring в `cmd` остался. Готово, если до изменений есть зафиксированный список импортов и понятен targeted test для каждого участка. ### GO-001.1 — webhook регистрации команды Файлы: `internal/app/team_registration.go`, `internal/services/team_service/registration.go` и связанные тесты. 1. Найти, какие sentinel-ошибки `TeamService.RegisterTeam` уже возвращает. 2. Сделать ошибку закрытой регистрации частью контракта `team_service`, чтобы handler не импортировал `teams_repo` только ради `errors.Is`. 3. Оставить `TeamRegistrationHandler` ответственным только за проверку bearer token, разбор JSON/JSON-RPC, timeout и HTTP-код. 4. Обновить тесты handler-а на публичную ошибку service; не переносить JSON parsing в service. Проверка этапа: ```sh cd evening_detective_server go test ./internal/app ./internal/services/team_service ! rg 'internal/repos' internal/app/team_registration.go ``` Ожидаемый результат: регистрация с закрытым окном по-прежнему даёт `409`, ошибочный payload — `400`, а app больше не знает `teams_repo`. ### GO-001.2 — выдача QR-кодов команд Файлы: `internal/app/team_qr.go`, `internal/services/team_service/service.go`, `team_qr.go` и тесты. 1. Перенести наружный смысл ошибок из repository/PDF-модуля на границу `team_service`: игра не найдена и у команды нет карточек. 2. Не переносить в service формирование `HttpBody` и HTTP-заголовков. 3. В handler оставить проверку claims/ролей, валидацию `id`, mapping ошибок в gRPC-коды и headers. 4. Проверить существующие тесты `team_qr_test.go` и добавить только недостающий regression case на обе ошибки. Проверка этапа: ```sh cd evening_detective_server go test ./internal/app ./internal/services/team_service ! rg 'internal/repos|team_qr_pdf' internal/app/team_qr.go ``` Ожидаемый результат: `DownloadTeamQR` сохраняет PDF и заголовки без прямой зависимости app от repository/PDF implementation. ### GO-001.3 — операции пользователей Файлы: `internal/app/users.go`, `internal/services/users_service/*` и тесты. 1. Найти, почему app использует `users_repo.ErrUserNotFound` только в `ChangeUserPassword`. 2. Передать наружу ошибку через контракт `users_service` (владелец прикладного смысла), не раскрывая в app storage-пакет. 3. Не менять здесь многочисленные protobuf-поля `Error`: их единое исправление относится к GO-003. 4. Оставить в app извлечение claims, пароля из metadata, роли и вызов service. 5. Обновить unit-тесты на `NotFound` через service-owned error. Проверка этапа: ```sh cd evening_detective_server go test ./internal/app ./internal/services/users_service ! rg 'internal/repos' internal/app/users.go ``` Ожидаемый результат: app не импортирует `users_repo`, а код gRPC для отсутствующего пользователя не меняется. ### GO-001.4 — архив сценария Файлы: `internal/app/scenario_archive.go`, `internal/services/scenarios_service/*` и тесты. 1. Отделить transport-часть от прикладной: app оставляет auth, `HttpBody`, `Content-Disposition` и проверку размера запроса. 2. Ошибку сценария не найдено получать через `scenarios_repo` в app; использовать service-owned контракт ошибки. 3. Проверить, нужен ли app доступ к `scenario_archive.MaxArchiveSize()` только как к transport-лимиту. Если да, предоставить лимит через service-owned константу/функцию; не тащить repository или storage-модель. 4. `string_tools` оставить только если это чистая transport-санитизация имени; иначе вернуть готовое безопасное имя из service. Не переносить бизнес-логику в mapper. 5. Не менять формат ZIP и правила авторизации. Проверка этапа: ```sh cd evening_detective_server go test ./internal/app ./internal/services/scenarios_service ! rg 'internal/repos' internal/app/scenario_archive.go ``` Ожидаемый результат: archive handler знает HTTP-представление, а не repository и внутреннюю реализацию архива. ### GO-001.5 — скачивание файлов Файлы: `internal/app/files.go`, `internal/services/file_service/*` и тесты. 1. Перенести `ErrFileNotFound` на публичную границу `file_service` (или сделать service-owned alias без нового слоя), чтобы handler не импортировал `file_storage`. 2. Оставить в app авторизацию автора, `nosniff`, `HttpBody` и mapping MIME-типа. 3. Проверить, что service уже возвращает полную модель файла и контекст; новых DTO и интерфейсов не добавлять. 4. Добавить/обновить тест на `404` скачивания отсутствующего файла. Проверка этапа: ```sh cd evening_detective_server go test ./internal/app ./internal/services/file_service ! rg 'internal/modules/file_storage' internal/app/files.go ``` ### GO-001.6 — app mapper-ы и чистка импортов Файлы: `internal/app/game_mapper.go`, `team_mapper.go`, `story_mapper.go`, `internal/app/*`. 1. Не переносить protobuf mapping в service: он остаётся в `app`. 2. Проверить, что входные типы mapper-ов являются контрактом service, а не типами repository/module. Если mapper использует `game_model` или `formatter_utils` только как промежуточный use-case тип, перенести границу в service минимальным изменением, сохранив существующий protobuf output. 3. Не переименовывать пакеты `*_utils` в рамках GO-001 — это GO-006. 4. После каждого переноса удалить неиспользуемые импорты и проверить callers. Проверка этапа: ```sh cd evening_detective_server find internal/app internal/services -name '*.go' -print0 | xargs -0 gofmt -l go vet ./... go test ./internal/app ./internal/services/... ``` ### GO-001.7 — финальная проверка архитектурного правила 1. Runtime-файлы `internal/app` не должны импортировать `internal/repos`. 2. Каждый RPC/HTTP handler должен вызывать service, а не repository/module с бизнес-операцией. 3. `cmd/evening_detective_server/main.go` продолжает собирать repositories, modules и services — его зависимости не удалять. 4. Проверить все callers изменённых ошибок и конструкторов. 5. Запустить полный набор проверок: ```sh cd evening_detective_server find internal/app internal/services -name '*.go' -print0 | xargs -0 gofmt -l go vet ./... go test ./... ``` 6. В `todo.md` отметить GO-001 выполненной только после успешной проверки за пределами sandbox, где разрешён loopback TCP. ## Критерий готовности GO-001 - Runtime `internal/app` не импортирует `internal/repos`. - В app не осталось прямого вызова repository или технического module для выполнения бизнес-операции. - Существующие transport-коды, headers, JSON payloads и protobuf responses не изменились намеренно. - Для каждого перенесённого error mapping есть targeted test. - `gofmt`, `go vet ./...` и полный `go test ./...` проходят в поддерживаемом окружении.