Почему бы написать статью в виде одного только агентского скилла. И вам полезно и мне не трудно.
Писал паттерн для себя. И для агента.
type-driven-legacy-refactoring
Универсальная методология рефакторинга и проектирования долгоживущих корпоративных систем с богатой бизнес-логикой на основе предварительного выявления намерений, археологии домена, функционального DDD Скотта Влашина и стратегии тестирования Майкла Физерса.
Применимость:
-
При рефакторинге и развитии долгоживущих продуктов, которые уже живут годами и будут развиваться дальше, где цена архитектурной ошибки крайне высока.
-
В проектах с высокой доменной сложностью и богатой бизнес-логикой (многостадийные визарды, финансовые/миграционные заявки, скоринги, биллинг).
-
Когда необходима «археология домена» — извлечение утраченных или неявных бизнес-правил из процедурного легаси-кода для их явной фиксации в новой архитектуре.
-
При жестких требованиях к надежности, отказоустойчивости (защита от разрывов сессий и сети) и статическим гарантиям корректности данных (Valid by Construction).
Навык агента (Agent Skill): Type-Driven Architecture & Refactoring via Intent Discovery, Wlaschin DDD & Feathers Testing
Этот навык представляет собой универсальный инженерный протокол проектирования и безопасного рефакторинга систем. Его ключевое отличие — обязательная предварительная фаза выявления намерений, приоритетов и архитектурных противоречий, а также опора на синтез теорий Скотта Влашина и Майкла Физерса.
ГРАНИЦЫ ПРИМЕНИМОСТИ (Где методология дает максимальный ROI)
Данный инженерный протокол является мощным архитектурным инструментом и дает стократный возврат инвестиций именно в следующих условиях:
-
Богатая бизнес-логика и высокая доменная сложность: Система содержит десятки взаимосвязанных правил, квот, ограничений и пошаговых переходов состояний.
-
Долгоживущие продукты (Long-Lived Core Products): Продукт жил годами, накопил процедурный долг и будет развиваться и поддерживаться еще долгие годы.
-
Необходимость «археологии домена»: Исходные требования или авторы утрачены, а реальные бизнес-правила и крайние случаи скрыты внутри тысяч строк легаси-скриптов и баз данных. Характеризационные тесты здесь выступают инструментом раскопки истины.
-
Высокие требования к надежности и архитектурным свойствам: Любой сбой в продакшене (Fatal Error, потеря сессии при вводе) недопустим из-за финансовых, юридических или репутационных рисков.
(Примечание: Для простых одноразовых CRUD-прототипов или быстрых MVP этот подход будет избыточным оверинжинирингом).
0. Фаза 0: Выявление намерений, приоритетов и разрешение противоречий (Intent & Contradiction Discovery)
Перед выбором архитектурных паттернов и написанием кода агент или архитектор обязан остановиться и провести диалог по выявлению намерений (intents), весов/приоритетов и скрытых противоречий.
Протокол Фазы 0:
-
Выявление намерений и приоритетов (Intents & Priorities):
-
Что важнее в данном контексте: абсолютная типобезопасность ядра, скорость клиентского UI-ввода, минимизация дублирования кода (DRY) или отказоустойчивость при сетевых сбоях?
-
Каковы ограничения среды (например: «у нас часто дохнет сессия», «обрывы сети при отправке виртуальных форм»)?
-
-
Картирование противоречий (Contradiction Mapping):
-
Конфликт 1: Требование UX (собрать и показать на веб-форме все ошибки ввода без падения) противоречит принципу доменной строгости Valid by Construction (мгновенно выбросить исключение при первой ошибке).
-
Конфликт 2: Требование фонового автосохранения каждые 15 секунд противоречит строгим реляционным ограничениям
NOT NULLи Foreign Key в боевых таблицах.
-
-
Методология Contradiction-Driven Synthesis (Поиск решений под противоречия): Поиск и анализ противоречий — это постоянный навык архитектора. Когда выявлен жесткий конфликт требований, сначала ищется решение в базовых каталогах (GoF, DDD). Если стандартные шаблоны не решают конфликт идеально, на основе матрицы приоритетов синтезируется специализированное архитектурное решение.
1. Канонические и специализированные паттерны архитектуры
1. Кейс-стади Contradiction-Driven Synthesis: Рождение паттерна Dual-Mode Specification
Чтобы методология поиска противоречий не оставалась абстрактной, разберем конкретный пример того, что именно анализировалось и почему синтезированное решение помогло:
-
Анализируемое жесткое противоречие трех сил:
-
Сила А (UX веб-формы): При пошаговом вводе сложной заявки система обязана собрать и вывести на экран абсолютно все ошибки одновременно (например, 10 проблемных полей из 20), не прерывая выполнение выбрасыванием исключения при первой ошибке.
-
Сила Б (Доменная строгость Valid by Construction): При конструировании финального доменного объекта конструктор обязан мгновенно отклонить невалидное состояние (выбросить
DomainExceptionпри первой ошибке), чтобы невалидный объект принципиально не мог существовать в памяти. -
Сила В (Единая точка правды / DRY): Бизнес-правила запрещено дублировать (писать отдельные проверки для UI и отдельные
if-guard проверки внутри конструкторов домена).
-
-
Почему стандартные шаблоны не подошли: Стандартный паттерн Спецификация (GoF) или классическая валидация контроллера решают только одну сторону (либо мягкий сбор ошибок для UI, либо жесткий Guard для домена).
-
Синтезированное решение (Dual-Mode Specification): Поскольку форма черновика и финальный агрегат имеют совпадающую структуру полей, был введен общий интерфейс чтения (
DomainReadContractInterface). Бизнес-правила реализуются один раз против этого интерфейса и запускаются через исполнитель (SpecificationEvaluator) в двух разных режимах:-
Collector Mode (для UI-контроллера): прогоняет объект через правила, накапливает все ошибки в
List<ValidationError>и возвращает их для отображения на форме без падения. -
Guard Mode (для фабрики доменной сущности): прогоняет объект через те же правила при конструировании. Если список ошибок не пуст, выбрасывает
DomainValidationException.
-
-
Почему это помогло: Противоречие было полностью разрешено: бизнес-правила написаны ровно 1 раз (DRY), UX формы показывает все ошибки разом, а доменные сущности на 100% защищены по принципу Valid by Construction.
2. Базовый каталог паттернов для смежных легаси-кейсов
В зависимости от вскрытых на Фазе 0 противоречий, применяются следующие канонические абстракции:
-
Изоляция и миграция легаси:
-
Strangler Fig / Branch by Abstraction: Плавное переключение вызовов со старого монолита на новый чистый модуль без переписывания системы за раз.
-
Shadow Read-Write (Dark Launch): Параллельная запись в старую и новую схему со сверкой результатов в фоне.
-
-
Укрощение процедурной сложности:
-
Strategy / Pipeline: Замена многоэтажных
if-elseи монолитных скриптов на цепочки взаимозаменяемых независимых классов-шагов. -
Facade: Единая чистая точка входа над клубком легаси-функций.
-
-
Целостность и надежность данных:
-
Immutable Type-State Builder: Моделирование пошаговых переходов через
readonly classс возвратом новых экземпляров (исключает побочные эффекты мутации in-place). -
Anti-Corruption Layer (ACL): Изоляция сервисов от сырых HTTP-массивов (
$_POST, JSON) через строгие DTO. -
Typed Collection (
List<T>): Обертывание списков дочерних сущностей для 100% навигации в IDE. -
Unit of Work / Transactional Outbox: Атомарная запись агрегата и гарантированная отправка внешних событий без потери данных при сбоях.
-
2. Теоретический фундамент: Функциональный DDD Скотта Влашина
Методология паттернов опирается на 5 столпов книги Скотта Влашина «Domain Modeling Made Functional»:
-
Язык предметной области через типы: Бизнес-состояния должны быть выражены типами (Enum, Readonly Struct), а не примитивными строками.
-
Make Illegal States Unrepresentable: Невалидный доменный объект не должен существовать в памяти.
-
Бизнес-процессы как чистые функции:
.
-
Защита границ: Очистка и валидация данных происходит на внешней границе системы (ACL).
-
Value Semantics: Отказ от мутации состояния в пользу функциональных трансформаций.
3. Методология безопасного тестирования и рефакторинга легаси по Майклу Физерсу (Michael Feathers)
Любая архитектурная трансформация легаси-кода обязана подчиняться правилам книги Майкла Физерса «Working Effectively with Legacy Code»:
1. Главный постулат Физерса
«Легаси-код — это просто код без тестов. Код без тестов плох тем, что его невозможно изменить с гарантией безопасности.»
2. Закон обратной пропорции качества легаси и затрат на начальное тестирование
Чем хуже качество легаси-кода (высокая запутанность, отсутствие типов, глобальные зависимости), тем больше времени и усилий придется потратить на начальное характеризационное тестирование и ручное создание эталонов (Golden Master).
Эта первоначальная инвестиция времени окупается вдвойне:
-
Защита от регрессий: Создает надежный каркас безопасности перед декомпозицией.
-
Археологическая раскопка домена (Выявление скрытых намерений): В процессе ручной фиксации эталонов изменений вскрываются скрытые бизнес-намерения, забытые политики и крайние случаи оркестрации, которые извлекаются из процедурного мусора и прописываются в новом чистом коде в явном виде.
3. Стратегия характеризационного тестирования (Golden Master)
-
Захват эталона (Golden Master Capture): Направьте на старый скрипт репрезентативные наборы входных данных (
$_POST/$_GET). -
Фиксация побочных эффектов: Запишите точные SQL-запросы, изменения в БД и сгенерированный HTML.
-
Сверка при рефакторинге: Запустите новый контроллер на тех же входных пейлоадах и убедитесь, что доменные результаты на 100% совпадают с эталоном.
4. Швы (Seams) и Ростки (Sprout Class / Sprout Method)
-
Внедрение швов (Object Seams): Замените прямые вызовы глобальных объектов на внедряемые интерфейсы (
DbConnection,LoggerInterface). -
Классы-ростки (Sprout Class): Выращивайте новую чистую функциональность в виде изолированных, 100% покрытых тестами классов-ростков и делегируйте им работу из легаси.
4. Пошаговый протокол выполнения
Шаг 0: Диалог намерений и противоречий (Intent & Contradiction Discovery)
-
Выясните ключевые бизнес-приоритеты заказчика и ограничения инфраструктуры продакшена.
-
Выявите и озвучьте архитектурные противоречия. Подберите или синтезируйте паттерны для их разрешения.
Шаг 1: Аудит источника правды, характеризационные тесты и выявление скрытых намерений
-
Изучите исходные легаси-файлы. Потратьте необходимое время на построение эталонов (Golden Master) по Майклу Физерсу.
-
Извлеките скрытые бизнес-намерения из поведения старого кода, чтобы зафиксировать их в новой архитектуре в явном виде.
Шаг 2: Создание Agile Spike на Rust
-
Напишите компактный концепт на Rust (
ARCHITECTURE_CONCEPT.rs). Опишите чистые структуры полей без гетеров, перечисления этапов UI и домена, стратегии восстановления (ErrorRecoveryStrategy) и трейты репозиториев.
Шаг 3: Проектирование слоя ACL, типизированных коллекций (List) и швов
-
Перенесите структуры в целевой язык в виде строгих
readonlyDTO. Замените массивы на спискиList<T>. -
Внедрите объектные швы (Seams) и ACL для изоляции домена от HTTP и глобальных переменных.
Шаг 4: Реализация универсального доменного ядра по Влашину и классам-росткам
-
Создайте общие интерфейсы чтения (
DomainReadContractInterface), атомарные правила (SpecificationRule) в виде классов-ростков и иммутабельные агрегаты (ValidAggregate).
Шаг 5: Изоляция представления и настройка IDE-навигации
-
Вынесите вёрстку и подсветку ошибок в изолированный класс вёрстки (
Render). Настройте единую точку входа (public/index.php), автозагрузчик и фабрику DI для 100% навигации в IDE.
Шаг 6: Защита продакшена, транзитные исключения и сверка с Golden Master
-
Проставьте точные аннотации
@throwsво всей цепочке вызовов. Постройте эшелоны отказоустойчивости. -
Прогоните характеризационные тесты и убедитесь в 100% совпадении с эталоном.
5. Антипаттерны (Чего делать КАТЕГОРИЧЕСКИ НЕЛЬЗЯ)
❌ Blind Coding without Phase 0: Написание кода или выбор паттернов без предварительного выявления намерений, приоритетов и противоречий заказчика.
❌ Refactoring without Characterization Tests: Изменение легаси-кода без инвестиции времени в фиксацию эталонного поведения (Golden Master) и выявления скрытых намерений.
❌ Legacy Mindset / Primitive Obsession: Использование сырых ассоциативных массивов (array $items) вместо DTO и типизированных коллекций List<T>.
❌ Duplicated Validation: Написание отдельных проверок для UI-формы и отдельных проверок внутри конструкторов домена.
❌ Anemic Valid Entity: Создание готовой сущности, которая является простой оберткой над DTO без проверки инвариантов при конструировании.
❌ In-Place Mutation: Прямое изменение полей ($draft->items[] = ...) вместо иммутабельных функциональных трансформаций.
❌ Boolean Blindness: Использование примитивных флажков bool $isError вместо перечислений (ErrorRecoveryStrategyEnum).
ссылка на оригинал статьи https://habr.com/ru/articles/1055616/