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 (кодирование условий строками): условие логики зашито в структуру кода вместо того, чтобы быть выражено поведением.

Решение

  1. Три явные фазы. Оркестрирующий метод структурируется как конфигурация (собрать всё нужное для работы) → последовательность действий (основной цикл/шаги) → обработка результата (сбор/лог/возврат). Фазы разделяются маркерами и/или вынесенными хелперами; их порядок читается сверху вниз.
  2. Применимость — именованный предикат-метод, не inline-конъюнкция. Вместо a != nil && b != nil && c != nil — метод, называющий смысл проверки (p.cursorStrategy() (strategy, available)).
  3. «Есть ли элемент» — хелпер, возвращающий (value, ok), не bool-флаг + ручной range по итератору (firstActiveCredential(...) (cred, ok, err)).
  4. Производные параметры — выделенные хелперы с типизированным результатом, не инлайн-сборка в теле (planFromSchedule(ctx) → tickSchedulePlan, openCursor(...)).
  5. Порты и неочевидные конфиг-поля документируются на русском: что это, как инициализируется, как выбирается (godoc на поле/интерфейсе).

Последствия

  • Метод читается как «настройка → работа → итог»; каждую фазу можно понять и протестировать отдельно; условия и существование выражены поведением (диалог вызовов), а не флагами/строками в теле.
  • Первое применение — RunOnce: фазовая структура + firstActiveCredential / cursorStrategy / openCursor / planFromSchedule (tickSchedulePlan); поведение не изменено (подтверждено unit-тестами ingestion + live-прогоном контура russvet).
  • Обобщает ADR-0066 (не кодировать условия строками) на структуру оркестраторов. Оба — конвенции code-review; кандидат на постепенный энфорсмент (сложные функции без фазового деления / inline-конъюнкции на портах — в рефакторинг-долг).

Связано

  • ADR-0066 — классификация/ветвление по типизированному коду, не по тексту.
  • ADR-0030 (композиция в DI-корне), ADR-0064 (чистые слои) — источники/стратегии/ротатор инъектируются, а не создаются в теле метода.