Работающий монолит нужно заменить без остановки бизнеса. Какой механизм миграции позволит безопасно переводить функциональность частями?
Используйте постепенное замещение: выделяйте функциональные срезы, направляйте их на новую реализацию, оставляя остальные запросы в монолите. Граница маршрутизации постепенно расширяется, пока старый участок не перестаёт обслуживать трафик и не удаляется.
Такой подход снижает риск большого разового переписывания. Однако он требует явной стратегии владения данными, совместимости контрактов, наблюдаемости и возможности быстро вернуть поток на старую реализацию.
Подход появился как ответ на риск масштабной замены старой системы одним выпуском. Большие переписывания часто затягиваются, скрытые зависимости обнаруживаются поздно, а бизнес получает изменения только в конце проекта.
Постепенная миграция переносит риск из одной крупной операции в серию контролируемых изменений. Каждая итерация даёт обратную связь о корректности новой реализации и позволяет ограничить область возможного сбоя.
Монолит обычно содержит не только бизнес-логику, но и общие таблицы, фоновые задачи, интеграции, правила авторизации и неявные зависимости. Поэтому простое копирование одного класса или сервиса не создаёт полноценной независимой части системы.
Неверная миграция может привести к расхождению данных, двойной обработке операций, различиям в правилах расчёта и невозможности понять, какая реализация сформировала результат. Дополнительный риск возникает, если новая и старая версии одновременно изменяют одни и те же данные без определённого владельца.
Сначала выбирают функциональный срез, который можно отделить через устойчивую точку перехвата: входной маршрут, фасад, очередь сообщений или другой контролируемый канал. Затем определяют границу ответственности новой реализации: какие операции, данные и побочные эффекты переходят к ней.
После этого маршрутизация переводится на новую реализацию для ограниченного трафика. Переключение может выполняться по функции, клиентской группе, региону или доле запросов, если такое разделение не нарушает бизнес-инварианты. Старый путь сохраняется как резервный, пока новая реализация не доказала корректность.
Особое внимание требуется данным. Предпочтительно, чтобы для каждого среза появился один явный владелец записи, а остальные участники обращались к нему через контракт. Временная синхронизация данных допустима как переходная мера, но её правила, задержки и обработка конфликтов должны быть явно определены.
Миграцию сопровождают проверками инвариантов, сравнением результатов старой и новой реализации на безопасном трафике, журналированием, метриками ошибок и процедурой отката. После перевода всех потребителей старый код, маршруты, схемы совместимости и переходные синхронизации нужно удалить; иначе временная архитектура станет постоянной.
Главный компромисс — временное усложнение системы. Некоторое время существуют две реализации, дополнительные маршруты и переходные интеграции, зато уменьшается размер отдельного изменения и появляется возможность остановить миграцию после любого среза. Подход плохо подходит, если систему нельзя разделить по поведению или если операции требуют неделимой общей транзакции через всю старую и новую части.
В монолите интернет-магазина нужно заменить расчёт доставки. Старый расчёт тесно связан с оформлением заказа, но его результат можно представить отдельным контрактом: стоимость, срок и причины невозможности доставки.
Рассматривались три варианта. Полное переписывание позволяло сразу устранить старые зависимости, но создавало большой риск и длинный цикл обратной связи. Параллельный запуск обеих реализаций на всех заказах давал хорошее сравнение, однако требовал защитить побочные эффекты и не допустить двойного обращения к перевозчику. Поэтапное переключение по регионам требовало временной маршрутизации, зато ограничивало последствия ошибки.
Выбрали третий вариант. Новая реализация сначала работала в режиме сравнения без влияния на заказ, затем стала основной для одного региона; для остальных сохранялся старый путь. Результаты сравнивались по стоимости, срокам и доле ошибок, а после подтверждения корректности регионы переводились последовательно.
Такой выбор обеспечил управляемый откат и позволил обнаружить различие в правилах округления до полного переключения. После перевода всех регионов старый расчёт удалили, а временный механизм сравнения отключили.
Выбирайте не самый крупный и не обязательно самый проблемный участок, а срез с понятным бизнес-контрактом, ограниченным числом зависимостей и измеримым результатом. Важно, чтобы его можно было перевести отдельно и проверить без участия большинства внутренних механизмов монолита.
Хороший первый срез одновременно снижает технический риск и проверяет сам способ миграции. Если он требует общей транзакции со всем монолитом или не имеет ясного владельца данных, это обычно плохой кандидат для начала.
Да, но только при явном разделении ролей. Запуск новой реализации для проверки может быть безопасным, если она не выполняет побочные эффекты либо эти эффекты идемпотентны и контролируются.
Нельзя бездумно дублировать списание денег, отправку уведомлений или изменение остатка. Для таких операций применяют режим только чтения, сравнение вычисленных результатов, тестовые контуры или заранее определённого владельца побочного эффекта.
Нужно сначала определить источник истины и переходный контракт между моделями. Возможны адаптер чтения старой схемы, разовая миграция данных, временная публикация изменений или поэтапная передача владения записями.
Дублирование данных само по себе не является решением: необходимо описать задержку, разрешение конфликтов, повторную обработку и момент прекращения синхронизации. Если согласованность должна быть строгой в одной транзакции, границу среза следует пересмотреть или временно сохранить операцию внутри монолита.