ADR-0068: Бизнес-параметры передаём явно, не через context.Context
Status: accepted Date: 2026-07-08 Deciders: Maxim Belkanov
Контекст
context.Context в ingestion используется не только по назначению. Через
app.WithSchedule в ctx кладётся доменная структура IngestionSchedule (job kind,
целевой набор SKU, бюджет тика, credential scope, customer/credential ref), а три
разных потребителя достают её из ctx и МЕНЯЮТ ПОВЕДЕНИЕ по ней:
bulk_snapshot_pipeline.go(planFromSchedule) — какую ось тянуть, каким бюджетом, какой срез;ingestion/di.go:393(credential provider) — выбрать customer-scoped кред вместо system-fallback;ingestion/di.go:589(rotator factory) — schedule-aware пул-ротатор.
Это скрытая («серая») передача параметров: значимый доменный вход не виден в
сигнатурах (RunTick(ctx, supplierRef), ListActive(ctx, supplier)), а протаскивается
неявно. Причина — так не пришлось менять сигнатуры интерфейсов/портов на всю глубину
вызова. Цена — код нельзя прочитать по сигнатурам, тесты вынуждены собирать ctx через
WithSchedule, а поведение зависит от невидимого значения.
Нюанс. WithSchedule делает две разные вещи: (1) прячет расписание ради ПОВЕДЕНИЯ —
анти-паттерн; (2) ставит platformerrors.JobScope (schedule_id/job_kind/customer_ref…)
для лог-корреляции — это ЛЕГИТИМНОЕ, кросс-срезовое использование ctx. Запрет касается
только (1).
Решение
- Доменные/бизнес-параметры передаются ЯВНО — аргументом функции или типизированным
request-структом (напр. «что тянуть в этот тик»), а не через
ctx.Value. - В
context.Contextдопустимо только: отмена/дедлайн; кросс-срезовые данные наблюдаемости и трассировки (correlation/JobScope для логов), не влияющие на выбор ветви логики; request-scoped идентификаторы, которые НЕ являются доменным входом. - Правило проверки: если по значению из ctx код выбирает, ЧТО и КАК делать (ось, бюджет, срез, scope креда) — это бизнес-параметр, его место в сигнатуре.
- Новый код обязан передавать такие параметры явно. Расширять
WithSchedule/ScheduleFromContext-паттерн (новые доменные поля в ctx, новые читатели) — нельзя.
Последствия
- Существующий долг (размотать):
IngestionScheduleв ctx (WithScheduleвscheduler_service.go:198; читатели — pipeline:533, credential providerdi.go:393, rotator factorydi.go:589; + тесты иinternal/tests/russvet_contourиспользуютWithScheduleза неимением явного пути). Целевая размотка: провести явный tick-request (schedule/job kind/бюджет/target/scope) черезPipelineRouter.RunTick→ конвейеры →RunOnce, и через портыCredentialProvider/RotatorFactory; убрать behavioural-чтение из ctx, оставив толькоJobScopeдля логов. Прод-критично (от scope зависит выбор customer-кредов) — делать отдельным аккуратным проходом с тестами, не «наскоком». - Пока путь не размотан,
contour-тест сознательно используетWithScheduleдля commerce-тика — это помечено как долг, а не образец.
Связано
- ADR-0066 (не кодировать условия строками), ADR-0067 (читаемость оркестраторов) — тот же принцип: логика выражается явными сигнатурами/поведением, а не спрятанным состоянием.
- ADR-0030 (композиция/инъекция в DI-корне): зависимости и их параметры — явные.