11 KiB
План 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 и границы
-
Проверить текущий список импортов:
rg -n 'internal/(repos|modules)' evening_detective_server/internal/app --glob '*.go' cd evening_detective_server && go test ./internal/app ./internal/services/... -
Выписать в diff только файлы из таблицы выше.
-
После каждого следующего этапа проверять, что импорт repository исчез только из app, а wiring в
cmdостался.
Готово, если до изменений есть зафиксированный список импортов и понятен targeted test для каждого участка.
GO-001.1 — webhook регистрации команды
Файлы: internal/app/team_registration.go,
internal/services/team_service/registration.go и связанные тесты.
- Найти, какие sentinel-ошибки
TeamService.RegisterTeamуже возвращает. - Сделать ошибку закрытой регистрации частью контракта
team_service, чтобы handler не импортировалteams_repoтолько радиerrors.Is. - Оставить
TeamRegistrationHandlerответственным только за проверку bearer token, разбор JSON/JSON-RPC, timeout и HTTP-код. - Обновить тесты handler-а на публичную ошибку service; не переносить JSON parsing в service.
Проверка этапа:
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 и тесты.
- Перенести наружный смысл ошибок из repository/PDF-модуля на границу
team_service: игра не найдена и у команды нет карточек. - Не переносить в service формирование
HttpBodyи HTTP-заголовков. - В handler оставить проверку claims/ролей, валидацию
id, mapping ошибок в gRPC-коды и headers. - Проверить существующие тесты
team_qr_test.goи добавить только недостающий regression case на обе ошибки.
Проверка этапа:
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/* и тесты.
- Найти, почему app использует
users_repo.ErrUserNotFoundтолько вChangeUserPassword. - Передать наружу ошибку через контракт
users_service(владелец прикладного смысла), не раскрывая в app storage-пакет. - Не менять здесь многочисленные protobuf-поля
Error: их единое исправление относится к GO-003. - Оставить в app извлечение claims, пароля из metadata, роли и вызов service.
- Обновить unit-тесты на
NotFoundчерез service-owned error.
Проверка этапа:
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/* и тесты.
- Отделить transport-часть от прикладной: app оставляет auth,
HttpBody,Content-Dispositionи проверку размера запроса. - Ошибку сценария не найдено получать через
scenarios_repoв app; использовать service-owned контракт ошибки. - Проверить, нужен ли app доступ к
scenario_archive.MaxArchiveSize()только как к transport-лимиту. Если да, предоставить лимит через service-owned константу/функцию; не тащить repository или storage-модель. string_toolsоставить только если это чистая transport-санитизация имени; иначе вернуть готовое безопасное имя из service. Не переносить бизнес-логику в mapper.- Не менять формат ZIP и правила авторизации.
Проверка этапа:
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/* и тесты.
- Перенести
ErrFileNotFoundна публичную границуfile_service(или сделать service-owned alias без нового слоя), чтобы handler не импортировалfile_storage. - Оставить в app авторизацию автора,
nosniff,HttpBodyи mapping MIME-типа. - Проверить, что service уже возвращает полную модель файла и контекст; новых DTO и интерфейсов не добавлять.
- Добавить/обновить тест на
404скачивания отсутствующего файла.
Проверка этапа:
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/*.
- Не переносить protobuf mapping в service: он остаётся в
app. - Проверить, что входные типы mapper-ов являются контрактом service, а не
типами repository/module. Если mapper использует
game_modelилиformatter_utilsтолько как промежуточный use-case тип, перенести границу в service минимальным изменением, сохранив существующий protobuf output. - Не переименовывать пакеты
*_utilsв рамках GO-001 — это GO-006. - После каждого переноса удалить неиспользуемые импорты и проверить callers.
Проверка этапа:
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 — финальная проверка архитектурного правила
-
Runtime-файлы
internal/appне должны импортироватьinternal/repos. -
Каждый RPC/HTTP handler должен вызывать service, а не repository/module с бизнес-операцией.
-
cmd/evening_detective_server/main.goпродолжает собирать repositories, modules и services — его зависимости не удалять. -
Проверить все callers изменённых ошибок и конструкторов.
-
Запустить полный набор проверок:
cd evening_detective_server find internal/app internal/services -name '*.go' -print0 | xargs -0 gofmt -l go vet ./... go test ./... -
В
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 ./...проходят в поддерживаемом окружении.