Skip links

От монолита к модульной архитектуре: наш реальный опыт миграции крупных продуктов на NestJS

Крупные legacy-монолиты в 2026 году всё ещё очень распространены — особенно в B2B SaaS, финтехе, enterprise-инструментах и маркетплейсах, которые запустились 5–10 лет назад. Они работают, приносят доход, но со временем становятся всё более болезненными: медленные релизы, страх трогать код, растущий технический долг, команды наступают друг другу на пятки.

За последние 18 месяцев мы успешно мигрировали три крупных продукта (от 400 тыс. до 1,2 млн+ строк TypeScript/Node.js) на модульную архитектуру NestJS. Вот честная история — что мы делали, какие паттерны выжили в продакшене, от чего отказались и какие измеримые бизнес-результаты получили.

Почему мы решились на миграцию (бизнес-триггеры)

Решение никогда не было «NestJS — это круто». Оно было продиктовано конкретной болью:

  • Среднее время релиза фичи — 3–6 недель (вместо 3–7 дней)
  • Один неудачный деплой мог заблокировать весь продукт на часы
  • Новым разработчикам требовалось 2–3 месяца, чтобы начать реально вносить вклад
  • Затраты на инфраструктуру росли быстрее трафика (масштабирование было all-or-nothing)
  • Бизнес хотел запускать независимые вертикали (новый движок ценообразования, отдельные white-label инстансы, встраиваемые модули для партнёров)

Нам нужны были независимая развёртываемость, чёткая ответственность команд и предсказуемая скорость — без полного переписывания.

Выбранная стратегия: Strangler Fig + Vertical Slice + модули NestJS

Мы сознательно не делали «большой взрывной реврайт». Вместо этого применили комбинацию трёх паттернов:

  1. Strangler Fig — постепенная замена частей старой системы новыми сервисами
  2. Vertical Slice Architecture — группировка по бизнес-возможностям, а не по техническим слоям
  3. NestJS как основа модулей и DI — сильная структура, отличная поддержка TypeScript, встроенная поддержка микросервисов

Как миграция выглядела на практике по фазам

Фаза 0 – Подготовка (1–2 месяца)

  • Создали общую библиотеку с авторизацией, логированием, конфигурацией, валидацией, обработкой ошибок
  • Ввели жёсткие правила ESLint + TypeScript для старого и нового кода
  • Настроили монопо (Turborepo + Nx) с кэшированием и affected-командами

Фаза 1 – Вынос критических путей чтения (3–5 месяцев) Первыми вынесли высоконагруженные read-only эндпоинты (листинги, дашборды, отчёты) в отдельные сервисы NestJS. Почему сначала чтение? Самый низкий риск, мгновенные выигрыши в производительности (edge-кэширование + Redis), быстрый фидбек.

Фаза 2 – Командная часть + событийно-ориентированное ядро (6–12 месяцев) Перенесли операции записи и бизнес-логику. Ключевое решение: domain events стали основным протоколом коммуникации между модулями (через RabbitMQ).

Фаза 3 – Независимая развёртываемость и автономия команд (продолжается) Каждая вертикаль (billing, users, content, analytics и т.д.) стала отдельным приложением NestJS со своим репозиторием, CI/CD, подмножеством схемы базы и пайплайном деплоя.

Архитектурные решения, которые выжили в продакшене

  • Модули NestJS как основная граница — каждый бизнес-домен = один модуль NestJS (позже вынесенный в самостоятельное приложение)
  • Shared kernel — очень тонкий (auth, config, logger, exceptions) — без бизнес-логики
  • Anti-corruption layer между старым монолитом и новыми сервисами
  • RabbitMQ + domain events — для всего асинхронного и межмодульного
  • Redis — везде: кэширование, rate limiting, хранилище сессий, временные блокировки
  • API Gateway (Kong / Traefik) + лёгкий service mesh (linkerd) для роутинга и observability
  • OpenTelemetry + Grafana Tempo — трассировка стала обязательной с первого дня

От чего мы попробовали и отказались

  • Полноценные микрофронтенды → слишком тяжело для наших кейсов (остались на module federation + Next.js)
  • GraphQL Federation → сложность превышала ценность (остались на REST + tRPC внутри экосистемы)
  • Отдельные базы данных на сервис с самого начала → слишком болезненно для транзакционной консистентности (оставили общий Postgres с schema-per-service + eventual consistency)
  • Kafka → overkill для нашего масштаба (RabbitMQ отлично справился со всем)

Измеримые результаты после 12–18 месяцев

  • Частота деплоев — с 1–2 в месяц → 15–40 в неделю (по вертикали)
  • Lead time for changes — с 18–42 дней → 2–7 дней
  • Mean time to recover — с 4–12 часов → менее 45 минут
  • Velocity — команды выдают 2,5–4× больше story points за спринт
  • Рост затрат на инфраструктуру — почти плоский при 2,8× росте трафика
  • Удовлетворённость разработчиков (внутренний NPS) — с 4,1 → 8,7

Текущий архитектурный набросок (2026)

  • Фронтенд → Next.js App Router + React Server Components
  • API-слой → Несколько приложений NestJS (по одному на крупный домен) + tRPC для внутренних вызовов
  • Коммуникация → RabbitMQ (события) + Redis (кэш и синхронизация)
  • Базы данных → PostgreSQL (основная) + ClickHouse (аналитика) + Redis
  • Развёртывание → Kubernetes + ArgoCD + edge-функции где возможно

Итог

Миграция крупного монолита на модульную архитектуру NestJS — непростая задача, но очень стоящая, когда рост бизнеса упирается в скорость инженерии.

Самые большие победы пришли не от самого NestJS (хотя его структура помогла колоссально), а от:

  • чётких границ ответственности
  • событийно-ориентированного мышления
  • безжалостного фокуса на развёртываемости
  • инкрементального подхода Strangler Fig

Если ваш продукт уже приносит доход, но разработка стала узким местом — начинайте с малого, сначала выносите чтение, измеряйте всё и не пытайтесь сделать идеально с первого раза.

Вы уже проходили миграцию монолита в модульную архитектуру? Что было самым сложным в вашем случае?

This website uses cookies to improve your web experience.