Монолит против микросервисов: как выбрать архитектуру в 2026 году

Почти целое десятилетие общепринятая мудрость в архитектуре ПО была простой: микросервисы — это будущее, а монолиты — наследие. Начинаете новый проект — строите микросервисы. Есть монолит — планируете его декомпозицию. Вопрос был не в том, переходить ли на микросервисы, а в том, как быстро вы туда доберётесь.

Этого консенсуса больше нет. Последние три года дали волну разборов от команд, которые внедрили микросервисы слишком рано, слишком агрессивно или не по тем причинам. Команда Amazon Prime Video опубликовала исследование, показавшее, что переход от бессерверных микросервисов к монолиту снизил затраты на 90%. InnoGames сообщила о сокращении сложности инфраструктуры вдвое после консолидации микросервисов обратно в монолит. Эти истории — не аномалии, а передний край коррекции.

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

Маятник качнулся обратно

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

Эти преимущества реальны, но у них есть цена, которую в годы ажиотажа систематически занижали. Каждый микросервис добавляет сетевую задержку, сложность распределённой системы, проблемы согласованности данных и операционные накладные расходы. У монолита один конвейер выкладки, одно приложение для наблюдения, одна база данных и одна кодовая база. У десяти микросервисов всего по десять, умноженное на точки интеграции между ними.

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

Микросервисы — хорошее решение для конкретного набора проблем. Если этих проблем у вас нет, вы платите за микросервисы, не получая выгоды. Самая дорогая архитектура — та, что решает проблемы, которых у вас нет.

Ландшафт 2026 года отражает эту коррекцию. Чистые микросервисные архитектуры теперь сосредоточены в организациях, которым они действительно нужны: крупные инженерные подразделения с десятками команд, платформы, где разные компоненты требуют независимого масштабирования, продукты, где у разных служб принципиально разные требования к надёжности или задержке. Во всех остальных случаях команды выбирают более простые архитектуры и держат микросервисы для тех участков, которые их действительно требуют.

Когда побеждает монолит

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

Размер команды — самый сильный предиктор архитектурного успеха. Если разработчиков меньше десяти, монолит почти наверняка правильный выбор. В маленькой команде координационные издержки микросервисов — согласование границ сервисов, поддержка межсервисных контрактов, ведение нескольких конвейеров выкладки — съедают заметную долю доступной инженерной мощности. Каждая граница сервиса — это контракт, который надо поддерживать, а поддержка контрактов не выпускает функции.

Стадия стартапа — ещё один ясный сигнал. Если продукту меньше двух лет или бизнес-модель ещё меняется, монолит сохраняет вашу способность быстро менять направление. Микросервисы фиксируют предположения о границах предметной области, которые в первый год вы почти наверняка сделаете неверно. Монолит позволяет свободно рефакторить. Когда приложение — одна кодовая база, перенос функции из модуля в модуль это операция рефакторинга. Когда приложение — десять сервисов, тот же перенос требует изменения интерфейсов, обновления потребителей, согласования выкладок и миграции данных.

# Как выглядит простая монолитная выкладка в 2026 году
# Один Dockerfile, один сервис, никакой оркестрации

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/server.js"]

# Один docker-compose.yml на весь стек
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://db:5432/app
  db:
    image: postgres:17
    volumes:
      - pgdata:/var/lib/postgresql/data

Операционную простоту такой схемы трудно переоценить. Один сервис для наблюдения, один поток логов для проверки, одна выкладка для отката. Младший разработчик способен понять весь конвейер за день. Когда что-то ломается, есть ровно одно место, где искать причину. Эта простота — не роскошь, а стратегическое преимущество, которое со временем только растёт.

Сложность предметной области — ещё один фактор в пользу монолита. Парадоксально, но чем сложнее область, тем опаснее преждевременная декомпозиция. Если разрезать сложную область на сервисы до того, как вы поняли её естественные границы, вы получите сервисы, связанные всеми неправильными способами: их нельзя выкатывать независимо, потому что изменение одного требует изменения другого; они делят базу данных, потому что данные не разделяются чисто; их приходится выкатывать синхронно, потому что контракты между ними постоянно меняются.

Модульный монолит: архитектура, которую большинство команд не рассматривает

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

Ключевое отличие модульного монолита от обычного — дисциплина. В обычном монолите модули ничем не удерживаются: любой код может импортировать любой другой, и со временем границы размываются в грязный ком. В модульном монолите у модулей есть явные публичные API и приватные реализации. Модуль A обращается к модулю B только через определённый интерфейс B. Прямой доступ к базе данных через границу модуля запрещён. Работают те же правила, что и в межсервисном взаимодействии, только общение идёт через вызовы функций, а не HTTP-запросы.

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

// Граница модуля в модульном монолите на TypeScript
// Каждый модуль отдаёт наружу только свой публичный API

// modules/orders/public-api.ts
export {
  createOrder,
  getOrderById,
  getOrdersByUser,
  OrderService,
} from "./order-service";

// modules/orders/internal/  ← всё здесь приватное
//   order-repository.ts
//   order-validator.ts
//   order-pricing.ts

// modules/payments/public-api.ts
export {
  processPayment,
  getPaymentStatus,
  refundPayment,
} from "./payment-service";

// Межмодульная зависимость явная и проверяемая
// payments/payment-service.ts импортирует из orders/public-api
import { getOrderById } from "../../orders/public-api";

Модульный монолит — ещё и лучшая страховка от неопределённого будущего. Если вы построили модульный монолит и позже обнаружили, что модуль должен стать самостоятельным сервисом, выделение окажется механическим: копируете код модуля в новый сервис, выставляете его публичный API через HTTP или очередь сообщений и подключаете вызывающую сторону. Границы модулей уже есть. Интерфейсы уже определены. Трудная работа — понимание границ предметной области — уже сделана.

Если же вы построили традиционный монолит без границ модулей и потом захотели выделить сервисы, задача становится куда сложнее. Сначала надо обнаружить, где границы должны проходить, затем отрефакторить код, чтобы он их соблюдал, и только потом выделять. Именно поэтому большинство миграций из монолита в микросервисы проваливается: команды недооценивают работу по обнаружению границ и получают микросервисы, связанные так, что вся затея теряет смысл.

  • Модульный монолит: одна единица выкладки, сетевые вызовы заменены вызовами функций, границы модулей соблюдаются, лёгкий путь к выделению сервисов.
  • Традиционный монолит: одна единица выкладки, границы не соблюдаются, максимальная свобода на раннем этапе, болезненный путь к выделению.
  • Микросервисы: много единиц выкладки, связь по сети, границы сервисов соблюдаются, высокая операционная стоимость.
  • Модульный монолит обычно лучшая отправная точка: он сохраняет возможности, не обязывая вас к сложности распределённой системы.

Когда микросервисы действительно имеют смысл

Микросервисы не ошибочны. Они ошибочны для большинства команд, но есть сценарии, где цена оправдана выгодой. Главное — честно ответить, ваш ли это сценарий.

Независимое масштабирование — самая защитимая причина. Если у разных частей системы кардинально разные профили нагрузки — API-шлюз должен обрабатывать 100 000 запросов в секунду, а служба отчётности 100 запросов в час, — держать их в одной единице выкладки расточительно. Железо под отчётность простаивает, а автомасштабирование шлюза упирается во время холодного старта отчётной службы. Отдельные службы масштабируются независимо, и экономия на эффективном использовании ресурсов может перекрыть операционные издержки.

Автономия команд — вторая законная причина. Когда над одной системой работает несколько команд и каждой нужно выкатываться в своём ритме, микросервисы снимают узкое место координации. Команда A выкатывается трижды в день, не дожидаясь, пока команда B завершит ревью. Но обратите внимание на порог: аргумент работает, только когда команд действительно несколько. Если во всей организации десять разработчиков, у вас нет проблемы координации, которую решают микросервисы. У вас проблема коммуникации, которую решает общий канал в мессенджере.

Разные требования к надёжности или задержке тоже оправдывают микросервисы. Если платёжной службе нужна доступность 99,999%, а аналитическая может терпеть эпизодические простои, их разделение гарантирует, что ошибка в отчётах не помешает клиентам оформить покупку. Так же и с задержкой: если одна часть системы требует предельно низкой задержки, а другая терпит высокую, разделение позволяет оптимизировать каждую независимо.

Технологическое разнообразие — самый слабый аргумент за микросервисы. Да, они позволяют использовать разные языки и базы данных для разных служб. Но на практике большинство организаций всё равно сходится на небольшом наборе технологий, и операционная стоимость поддержки нескольких сред выполнения обычно превышает выгоду. Если вся команда знает TypeScript и PostgreSQL, писать один сервис на Rust, а другой на Go просто ради разнообразия — роскошь, которую большинство организаций себе позволить не может.

Шаблон «начни с монолита, выделяй сервисы»

Самый надёжный шаблон построения систем в 2026 году одновременно и самый простой: начните с модульного монолита, затем выделяйте сервисы, когда появятся доказательства, что они нужны. Его называют «монолит первым» или «выделение микросервисов», и он стал рекомендацией по умолчанию у организаций, которые прошли через микросервисный ажиотаж и выжили.

Шаблон работает в четыре фазы. Первая — модульный монолит. Вы строите всё приложение как одну единицу выкладки со строгими границами модулей. Каждый модуль владеет своими данными, выставляет публичный API и держит реализацию приватной. Дисциплина та же, что и для микросервисов — ясные контракты, разделённое владение данными, явные зависимости, — но всё работает в одном процессе.

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

Третья фаза — выделение. Вы берёте модуль, доказавший, что ему нужно быть сервисом — потому что частота его изменений вызывает слишком много выкладок монолита или его требования к масштабированию отличаются от остальной системы, — и выделяете его. Поскольку у модуля уже чистые границы, выделение механическое. Вы создаёте новый сервис со своим конвейером выкладки, выставляете публичный API модуля через HTTP или очередь сообщений и правите монолит так, чтобы он вызывал новый сервис вместо модуля напрямую.

// Шаг 1: обозначьте кандидата на выделение как модуль
// monolith/src/modules/reports/public-api.ts
export async function generateReport(
  reportId: string
): Promise<ReportResult> {
  // Деталь реализации: читает из отдельной реплики,
  // занимает 30 секунд, не должен блокировать основное приложение
}

// Шаг 2: когда данные показывают, что это должен быть сервис:
// 1. Создайте новый сервис из кода модуля
// 2. Выставьте тот же API через HTTP
// 3. Замените прямой вызов на клиент сервиса

// monolith/src/clients/reporting-service.ts
const client = new ServiceClient({
  name: "reporting",
  baseUrl: process.env.REPORTING_SERVICE_URL,
  timeout: 60000, // этот сервис медленный
});

export async function generateReport(reportId: string) {
  return client.post("/reports", { reportId });
}

// Монолит не нужно переписывать — клиент сам берёт на себя
// повторные попытки, тайм-ауты и размыкание цепи.

Четвёртая фаза — повторение. По мере роста системы вы повторяете цикл: измерить, выделить, измерить снова. Некоторые выделенные сервисы позже придётся разбить на более мелкие. Некоторые, возможно, придётся вернуть обратно в монолит, если выделение не принесло ценности. Главное — каждое выделение опирается на данные, а не на архитектурную догму.

У этого шаблона есть одно критическое преимущество перед подходом «микросервисы первыми»: он откладывает необратимые решения. Каждая созданная граница сервиса — необратимое обязательство перед сложностью распределённой системы. Как только сервис существует, его границы уже не поменять, не сломав клиентов. Начиная с монолита и выделяя только по необходимости, вы гарантируете, что каждая граница оправдана реальными требованиями, а не догадками о будущих нуждах.

Как выбрать: рамка принятия решения

Проектируя новую систему или оценивая текущую архитектуру, пройдите по этим вопросам по порядку. Ответы укажут на правильную архитектуру, и предсказывать будущее не придётся.

Первый вопрос: сколько разработчиков работает над системой? Если меньше десяти — начинайте с монолита, желательно модульного. У вас нет проблемы координации, которую решают микросервисы, и вы не можете позволить себе операционные издержки. Если больше десяти, ответ зависит от того, как они организованы. Работают как одна команда — монолит по-прежнему уместен. Разбиты на несколько автономных команд — микросервисы, возможно, стоит рассмотреть.

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

Третий вопрос: можно ли выкатывать все части системы по одному графику? Если да, монолит упрощает конвейер и снижает стоимость координации. Если нет — потому что у разных частей разные циклы релизов, нормативные требования или профили риска, — микросервисы позволяют каждой части идти в своём ритме.

Четвёртый вопрос: что произойдёт, если все части системы откажут одновременно? Если ответ «бизнес полностью встанет», значит отказоустойчивости от микросервисов вы не получаете — вы только платите за них. Настоящая отказоустойчивость требует не просто отдельных сервисов, но отдельной инфраструктуры, отдельных хранилищ данных и мягкой деградации между сервисами. Большинство команд этого не строят. Они строят сервисы, тесно связанные в выкладке и слабо связанные в теории, — худшее из обоих миров.

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

Пятый вопрос: насколько вы уверены в границах своей предметной области? Если вы строите в хорошо изученной области с устоявшимися шаблонами — электронная коммерция, управление контентом, биллинг, — границы относительно стабильны, и микросервисы менее рискованны. Если область новая и границы ещё формируются, монолит сохраняет способность рефакторить по мере того, как понимание растёт. Преждевременные границы сервисов превращаются в ограничения, которые тормозят ровно тот процесс обучения, который вам нужно пройти.

Честный ответ для большинства команд в 2026 году — модульный монолит. Он даёт дисциплину микросервисов без их операционной стоимости. Он сохраняет возможность выделить сервисы позже, не принуждая вас к сложности распределённой системы сегодня. Он выкатывается одним разработчиком, отлаживается по одному потоку логов и меняется одним пул-реквестом. А если система вырастет настолько, что монолит перестанет справляться, построенные границы модулей сделают переход к микросервисам глаже, чем вы ожидаете.

Архитектурный маятник качнулся обратно к простоте. Это не регресс. Это индустрия, которая учится на опыте. Команды, которые не поддались ажиотажу — или быстро от него оправились, — это те, кто выпускает функции, а не мигрирует сервисы. Выберите архитектуру, которая позволяет выпускать, и меняйте её только тогда, когда система сама скажет, что пора.

See what your own repository can account for.

Thirty minutes on a repository you choose, including the part the record cannot attribute.