ADR-0067: Читаемость оркестрирующих методов — конфигурация → действия → результат
Status: accepted Date: 2026-07-08 Deciders: Maxim Belkanov
Контекст
Оркестрирующие методы ingestion (эталонный случай — BulkSnapshotPipelineImpl.RunOnce)
разрослись в «спагетти»: сбор конфигурации (источник, креды, расписание, ротатор,
курсор), основной цикл обхода источника и обработка результата перемешаны в одном
теле на ~190 строк. Условия закодированы inline и плохо читаются/ревьюятся:
- применимость курсора — конъюнкция из трёх cfg-полей прямо в строке
(
strategy != nil && Cursors != nil && Resolver != nil); - «есть ли активный кред» —
bool-флаг + ручнойrangeпо итератору; - параметры опроса (job kind / бюджет / целевой набор) собираются инлайн в теле;
- неочевидные конфиг-поля и доменные порты (
Sources,Strategies,CredentialRotator,IncrementalSyncKind, расписание) без внятного описания — по коду не понять, что это и как инициализируется.
Это тот же класс проблемы, что и ADR-0066 (кодирование условий строками): условие логики зашито в структуру кода вместо того, чтобы быть выражено поведением.
Решение
- Три явные фазы. Оркестрирующий метод структурируется как конфигурация (собрать всё нужное для работы) → последовательность действий (основной цикл/шаги) → обработка результата (сбор/лог/возврат). Фазы разделяются маркерами и/или вынесенными хелперами; их порядок читается сверху вниз.
- Применимость — именованный предикат-метод, не inline-конъюнкция. Вместо
a != nil && b != nil && c != nil— метод, называющий смысл проверки (p.cursorStrategy() (strategy, available)). - «Есть ли элемент» — хелпер, возвращающий
(value, ok), неbool-флаг + ручнойrangeпо итератору (firstActiveCredential(...) (cred, ok, err)). - Производные параметры — выделенные хелперы с типизированным результатом, не
инлайн-сборка в теле (
planFromSchedule(ctx) → tickSchedulePlan,openCursor(...)). - Порты и неочевидные конфиг-поля документируются на русском: что это, как инициализируется, как выбирается (godoc на поле/интерфейсе).
Последствия
- Метод читается как «настройка → работа → итог»; каждую фазу можно понять и протестировать отдельно; условия и существование выражены поведением (диалог вызовов), а не флагами/строками в теле.
- Первое применение —
RunOnce: фазовая структура +firstActiveCredential/cursorStrategy/openCursor/planFromSchedule(tickSchedulePlan); поведение не изменено (подтверждено unit-тестами ingestion + live-прогоном контура russvet). - Обобщает ADR-0066 (не кодировать условия строками) на структуру оркестраторов. Оба — конвенции code-review; кандидат на постепенный энфорсмент (сложные функции без фазового деления / inline-конъюнкции на портах — в рефакторинг-долг).
Связано
- ADR-0066 — классификация/ветвление по типизированному коду, не по тексту.
- ADR-0030 (композиция в DI-корне), ADR-0064 (чистые слои) — источники/стратегии/ротатор инъектируются, а не создаются в теле метода.