При проектировании приложения часто приходится выбирать, как организовать его серверную часть: оставить единым приложением или разделить на несколько независимых сервисов. Отсюда появляются понятия клиент-серверной, монолитной и микросервисной архитектур.
При этом клиент-серверную и микросервисную архитектуры неправильно воспринимать как альтернативы. Они описывают разные уровни системы: первая — взаимодействие клиента с сервером, вторая — устройство серверной части.
Разберемся, как эти подходы связаны между собой, чем отличается монолит от микросервисов и в какой момент разделение системы действительно оправдано.
При этом клиент-серверную и микросервисную архитектуры неправильно воспринимать как альтернативы. Они описывают разные уровни системы: первая — взаимодействие клиента с сервером, вторая — устройство серверной части.
Разберемся, как эти подходы связаны между собой, чем отличается монолит от микросервисов и в какой момент разделение системы действительно оправдано.
Клиент-серверная архитектура
Клиент-серверная архитектура строится вокруг двух основных ролей. Клиент инициирует запрос, а сервер принимает его, выполняет необходимую обработку и возвращает результат.
В веб-приложении клиентом обычно выступает браузер или мобильное приложение, а сервер предоставляет API и реализует бизнес-логику.
Упрощенно взаимодействие выглядит так:
Клиент → HTTP-запрос → Сервер → обработка → HTTP-ответ → Клиент
Например, пользователь открывает страницу интернет-магазина. Frontend отправляет запрос GET /products, сервер получает данные о товарах и возвращает их клиенту. Пользователь при этом не знает, где именно хранятся данные и какие операции сервер выполнил перед формированием ответа.
Главная идея такой архитектуры — разделение ответственности. Клиент отвечает за взаимодействие с пользователем и представление данных, сервер — за бизнес-логику, работу с данными и предоставление API.
При этом клиент-серверная архитектура ничего не говорит о том, что происходит внутри сервера. Сервер может быть одним приложением, несколькими сервисами или вообще представлять собой сложную распределенную систему.
В веб-приложении клиентом обычно выступает браузер или мобильное приложение, а сервер предоставляет API и реализует бизнес-логику.
Упрощенно взаимодействие выглядит так:
Клиент → HTTP-запрос → Сервер → обработка → HTTP-ответ → Клиент
Например, пользователь открывает страницу интернет-магазина. Frontend отправляет запрос GET /products, сервер получает данные о товарах и возвращает их клиенту. Пользователь при этом не знает, где именно хранятся данные и какие операции сервер выполнил перед формированием ответа.
Главная идея такой архитектуры — разделение ответственности. Клиент отвечает за взаимодействие с пользователем и представление данных, сервер — за бизнес-логику, работу с данными и предоставление API.
При этом клиент-серверная архитектура ничего не говорит о том, что происходит внутри сервера. Сервер может быть одним приложением, несколькими сервисами или вообще представлять собой сложную распределенную систему.
Монолит: один сервер — одно приложение
Один из вариантов реализации серверной части — монолит. В этом случае бизнес-логика приложения находится в одном разворачиваемом приложении.
Для интернет-магазина это может быть Java-приложение, внутри которого есть модули пользователей, каталога, корзины, заказов и платежей:
Frontend → Java-приложение → база данных
При этом внутри монолита вполне могут существовать четкие модули и границы ответственности. Монолит не означает автоматически плохо спроектированный или неструктурированный код.
Его главное преимущество — относительная простота. У приложения одна кодовая база, один основной процесс и относительно простая схема взаимодействия между компонентами. Вызов одного модуля из другого не требует сетевого соединения, а транзакции проще организовать на уровне одной базы данных.
Проблемы обычно появляются с ростом системы. Если приложение становится большим, его сложнее изменять и тестировать. Разные команды начинают конфликтовать за одни и те же части кодовой базы, а отдельное масштабирование компонентов становится невозможным.
Например, если каталог получает в десять раз больше запросов, чем оформление заказов, при обычном монолите нельзя независимо масштабировать только каталог, придется масштабировать все приложение.
Именно здесь микросервисная архитектура может стать одним из вариантов решения.
Для интернет-магазина это может быть Java-приложение, внутри которого есть модули пользователей, каталога, корзины, заказов и платежей:
Frontend → Java-приложение → база данных
При этом внутри монолита вполне могут существовать четкие модули и границы ответственности. Монолит не означает автоматически плохо спроектированный или неструктурированный код.
Его главное преимущество — относительная простота. У приложения одна кодовая база, один основной процесс и относительно простая схема взаимодействия между компонентами. Вызов одного модуля из другого не требует сетевого соединения, а транзакции проще организовать на уровне одной базы данных.
Проблемы обычно появляются с ростом системы. Если приложение становится большим, его сложнее изменять и тестировать. Разные команды начинают конфликтовать за одни и те же части кодовой базы, а отдельное масштабирование компонентов становится невозможным.
Например, если каталог получает в десять раз больше запросов, чем оформление заказов, при обычном монолите нельзя независимо масштабировать только каталог, придется масштабировать все приложение.
Именно здесь микросервисная архитектура может стать одним из вариантов решения.
Что такое микросервисная архитектура
В микросервисной архитектуре приложение состоит из нескольких автономных сервисов, каждый из которых отвечает за определенную бизнес-функцию.
Тот же интернет-магазин можно разделить на сервисы:
Frontend → API Gateway → Catalog Service
** ↘ Order Service**
** ↘ User Service**
** ↘ Payment Service**
Каждый сервис представляет собой отдельное приложение и может иметь собственный жизненный цикл: разрабатываться, тестироваться, разворачиваться и масштабироваться независимо.
При этом главное в микросервисах — не размер сервиса и не количество строк кода. Важнее границы ответственности. Сервис должен представлять относительно самостоятельную бизнес-возможность и иметь четкий контракт взаимодействия с остальной системой.
Например, сервис заказов отвечает за создание и изменение заказов, а сервис пользователей — за учетные записи и связанные с ними операции. Сервис заказов не должен напрямую менять таблицы сервиса пользователей только потому, что обе части системы используют одну СУБД.
Тот же интернет-магазин можно разделить на сервисы:
Frontend → API Gateway → Catalog Service
** ↘ Order Service**
** ↘ User Service**
** ↘ Payment Service**
Каждый сервис представляет собой отдельное приложение и может иметь собственный жизненный цикл: разрабатываться, тестироваться, разворачиваться и масштабироваться независимо.
При этом главное в микросервисах — не размер сервиса и не количество строк кода. Важнее границы ответственности. Сервис должен представлять относительно самостоятельную бизнес-возможность и иметь четкий контракт взаимодействия с остальной системой.
Например, сервис заказов отвечает за создание и изменение заказов, а сервис пользователей — за учетные записи и связанные с ними операции. Сервис заказов не должен напрямую менять таблицы сервиса пользователей только потому, что обе части системы используют одну СУБД.
Как микросервисы взаимодействуют
После разделения приложения появляется проблема, которой почти не было внутри монолита: сервисам нужно взаимодействовать по сети.
Разница принципиальная. При синхронном вызове сервис обычно ждет ответ другого сервиса. При асинхронном взаимодействии отправитель может продолжить работу, не дожидаясь обработки события.
Но распределенная система добавляет новые сценарии отказа. Сервис может быть временно недоступен, запрос может завершиться таймаутом, сообщение может прийти повторно, а один компонент может обновиться раньше другого.
Поэтому при проектировании микросервисов приходится отдельно продумывать таймауты, retry, идемпотентность, обработку ошибок и отказоустойчивость.
- Для синхронного взаимодействия часто используют HTTP/REST или gRPC. Например, Order Service может запросить у Catalog Service информацию о товаре.
- Другой вариант — асинхронное взаимодействие через брокер сообщений. После создания заказа сервис заказов может опубликовать событие OrderCreated. Его обработают другие сервисы: например, сервис уведомлений отправит сообщение пользователю, а аналитический сервис сохранит событие для дальнейшего анализа.
Разница принципиальная. При синхронном вызове сервис обычно ждет ответ другого сервиса. При асинхронном взаимодействии отправитель может продолжить работу, не дожидаясь обработки события.
Но распределенная система добавляет новые сценарии отказа. Сервис может быть временно недоступен, запрос может завершиться таймаутом, сообщение может прийти повторно, а один компонент может обновиться раньше другого.
Поэтому при проектировании микросервисов приходится отдельно продумывать таймауты, retry, идемпотентность, обработку ошибок и отказоустойчивость.
Что происходит с базами данных
В монолите несколько модулей могут работать с одной базой:
Application → Database
В микросервисной архитектуре обычно стремятся к принципу database per service: каждый сервис является владельцем своих данных, а другие сервисы получают необходимую информацию через API или события.
Например:
Order Service → Orders DB
User Service → Users DB
Catalog Service → Catalog DB
Это позволяет сервисам быть более независимыми, но усложняет работу с данными.
Допустим, оформление заказа требует информации о пользователе, товаре и оплате. В монолите подобная операция может выполняться в рамках одной базы и одной транзакции. В микросервисной системе данные распределены между несколькими владельцами.
Поэтому приходится учитывать распределенные транзакции и eventual consistency — ситуацию, когда разные части системы могут прийти к согласованному состоянию не одновременно.
Например, заказ уже создан, но событие об этом еще не обработано сервисом уведомлений. Для пользователя это может означать, что заказ появился в личном кабинете раньше, чем пришло уведомление о его оформлении.
Application → Database
В микросервисной архитектуре обычно стремятся к принципу database per service: каждый сервис является владельцем своих данных, а другие сервисы получают необходимую информацию через API или события.
Например:
Order Service → Orders DB
User Service → Users DB
Catalog Service → Catalog DB
Это позволяет сервисам быть более независимыми, но усложняет работу с данными.
Допустим, оформление заказа требует информации о пользователе, товаре и оплате. В монолите подобная операция может выполняться в рамках одной базы и одной транзакции. В микросервисной системе данные распределены между несколькими владельцами.
Поэтому приходится учитывать распределенные транзакции и eventual consistency — ситуацию, когда разные части системы могут прийти к согласованному состоянию не одновременно.
Например, заказ уже создан, но событие об этом еще не обработано сервисом уведомлений. Для пользователя это может означать, что заказ появился в личном кабинете раньше, чем пришло уведомление о его оформлении.
Зачем нужен API Gateway
Если frontend взаимодействует непосредственно с каждым микросервисом, клиенту придется знать внутреннюю структуру системы:
Frontend → Catalog Service
Frontend → Order Service
Frontend → User Service
Frontend → Payment Service
Это создает дополнительную связанность. Поэтому между клиентом и сервисами часто размещают API Gateway.
Теперь клиент взаимодействует с единой точкой входа:
Frontend → API Gateway → Microservices
Gateway может маршрутизировать запросы, выполнять аутентификацию и авторизацию, ограничивать частоту запросов и в некоторых случаях агрегировать данные нескольких сервисов.
При этом API Gateway не является обязательным признаком микросервисной архитектуры. Это архитектурный инструмент, который используют, когда он решает конкретные задачи системы.
Frontend → Catalog Service
Frontend → Order Service
Frontend → User Service
Frontend → Payment Service
Это создает дополнительную связанность. Поэтому между клиентом и сервисами часто размещают API Gateway.
Теперь клиент взаимодействует с единой точкой входа:
Frontend → API Gateway → Microservices
Gateway может маршрутизировать запросы, выполнять аутентификацию и авторизацию, ограничивать частоту запросов и в некоторых случаях агрегировать данные нескольких сервисов.
При этом API Gateway не является обязательным признаком микросервисной архитектуры. Это архитектурный инструмент, который используют, когда он решает конкретные задачи системы.
Масштабирование: одно из ключевых отличий
Представим интернет-магазин во время крупной распродажи. Каталог получает огромный поток запросов, а сервис оплаты работает с обычной нагрузкой.
В монолите приходится масштабировать все приложение:
Application × 10
В микросервисной архитектуре можно отдельно увеличить количество экземпляров Catalog Service:
Catalog Service × 10
Order Service × 3
Payment Service × 2
Такой подход позволяет эффективнее использовать ресурсы, если компоненты системы действительно имеют разные профили нагрузки.
Но микросервисы не делают масштабирование бесплатным. Вместе с количеством сервисов растет количество инфраструктурных компонентов, сетевых взаимодействий и потенциальных точек отказа.
В монолите приходится масштабировать все приложение:
Application × 10
В микросервисной архитектуре можно отдельно увеличить количество экземпляров Catalog Service:
Catalog Service × 10
Order Service × 3
Payment Service × 2
Такой подход позволяет эффективнее использовать ресурсы, если компоненты системы действительно имеют разные профили нагрузки.
Но микросервисы не делают масштабирование бесплатным. Вместе с количеством сервисов растет количество инфраструктурных компонентов, сетевых взаимодействий и потенциальных точек отказа.
Мониторинг и эксплуатация
Чем больше сервисов в системе, тем сложнее понять, что произошло с конкретным пользовательским запросом.
Например:
Frontend → API Gateway → Order Service → Payment Service → Notification Service
Если пользователь получил ошибку при оформлении заказа, недостаточно посмотреть лог только Order Service. Проблема могла возникнуть в Payment Service или на уровне сетевого взаимодействия между компонентами.
Поэтому для микросервисных систем особенно важны:
Вместо одного приложения команда фактически начинает управлять распределенной системой. Поэтому микросервисная архитектура тесно связана с DevOps-практиками и автоматизацией инфраструктуры.
Например:
Frontend → API Gateway → Order Service → Payment Service → Notification Service
Если пользователь получил ошибку при оформлении заказа, недостаточно посмотреть лог только Order Service. Проблема могла возникнуть в Payment Service или на уровне сетевого взаимодействия между компонентами.
Поэтому для микросервисных систем особенно важны:
- Централизованные логи;
- Метрики;
- distributed tracing;
- correlation/request ID;
- Мониторинг доступности сервисов;
- Автоматизированный CI/CD.
Вместо одного приложения команда фактически начинает управлять распределенной системой. Поэтому микросервисная архитектура тесно связана с DevOps-практиками и автоматизацией инфраструктуры.
Монолит и микросервисы: что выбрать
Универсального победителя здесь нет. Выбор зависит от размера системы, бизнес-требований, команды и ожидаемой нагрузки.
Для небольшого продукта с одной командой хорошо спроектированный монолит часто будет рациональнее микросервисов. Здесь можно использовать подход Monolith First: сначала реализовать систему как монолит, а затем, по мере появления реальных потребностей, выделять в отдельные сервисы те компоненты, для которых независимое масштабирование, разработка или деплой действительно дают преимущества.
Такой подход позволяет не добавлять сложность распределенной системы заранее. На старте команда работает с одной кодовой базой, одной основной точкой деплоя и более простым взаимодействием между компонентами. При этом архитектуру монолита стоит проектировать с четкими границами между модулями — тогда при необходимости отдельные части системы будет проще выделить в самостоятельные сервисы.
Микросервисы становятся особенно полезны, когда система растет, разные части имеют разную нагрузку, несколько команд должны независимо выпускать изменения или отдельные бизнес-компоненты требуют самостоятельного масштабирования.
При этом переходить на микросервисы только потому, что «так делают современные компании», не стоит. Распределенная архитектура сама по себе создает дополнительную сложность, которую придется поддерживать на протяжении всего жизненного цикла продукта.
Такой подход позволяет не добавлять сложность распределенной системы заранее. На старте команда работает с одной кодовой базой, одной основной точкой деплоя и более простым взаимодействием между компонентами. При этом архитектуру монолита стоит проектировать с четкими границами между модулями — тогда при необходимости отдельные части системы будет проще выделить в самостоятельные сервисы.
Микросервисы становятся особенно полезны, когда система растет, разные части имеют разную нагрузку, несколько команд должны независимо выпускать изменения или отдельные бизнес-компоненты требуют самостоятельного масштабирования.
При этом переходить на микросервисы только потому, что «так делают современные компании», не стоит. Распределенная архитектура сама по себе создает дополнительную сложность, которую придется поддерживать на протяжении всего жизненного цикла продукта.
Клиент-серверная и микросервисная архитектуры — не конкуренты
Здесь важно вернуться к главному вопросу.
Поэтому вполне нормальная система может одновременно быть клиент-серверной и микросервисной:
Web / Mobile Client → API Gateway → Microservices → Databases
Точно так же клиент-серверное приложение может использовать монолит:
Web / Mobile Client → Monolith → Database
То есть противопоставлять «клиент-серверную архитектуру» и «микросервисы» некорректно. Это разные уровни описания системы.
- Клиент-серверная архитектура отвечает на вопрос: какой вид взаимодействия используется между участниками системы? В этой модели клиент инициирует запрос, а сервер его обрабатывает и возвращает ответ — то есть клиент просит, сервер отвечает.
- Микросервисная архитектура отвечает на другой вопрос: как организована серверная часть внутри?
Поэтому вполне нормальная система может одновременно быть клиент-серверной и микросервисной:
Web / Mobile Client → API Gateway → Microservices → Databases
Точно так же клиент-серверное приложение может использовать монолит:
Web / Mobile Client → Monolith → Database
То есть противопоставлять «клиент-серверную архитектуру» и «микросервисы» некорректно. Это разные уровни описания системы.
Архитектуру стоит выбирать не от технологий, а от требований системы. Перед декомпозицией полезно определить, какие бизнес-функции действительно являются самостоятельными, как они взаимодействуют, кому принадлежат данные и какие части системы должны масштабироваться независимо.
Если сервисы постоянно обращаются друг к другу, используют общую базу и не могут выпускаться независимо, формальное разделение монолита на несколько приложений вряд ли даст ожидаемые преимущества.
Хорошая микросервисная архитектура — это не «как можно больше маленьких сервисов». Это система с понятными границами ответственности, контролируемыми зависимостями и обоснованной степенью автономности компонентов.
В конечном счете выбор выглядит не как «монолит или микросервисы», а как поиск подходящей архитектуры под конкретный продукт. Для одного проекта лучшим решением будет простой монолит, для другого — набор независимых сервисов. Главное, чтобы архитектурное решение отвечало реальным требованиям системы, а не само становилось источником ненужной сложности.
Хотите узнать больше? Изучите другие статьи из раздела:
Если сервисы постоянно обращаются друг к другу, используют общую базу и не могут выпускаться независимо, формальное разделение монолита на несколько приложений вряд ли даст ожидаемые преимущества.
Хорошая микросервисная архитектура — это не «как можно больше маленьких сервисов». Это система с понятными границами ответственности, контролируемыми зависимостями и обоснованной степенью автономности компонентов.
В конечном счете выбор выглядит не как «монолит или микросервисы», а как поиск подходящей архитектуры под конкретный продукт. Для одного проекта лучшим решением будет простой монолит, для другого — набор независимых сервисов. Главное, чтобы архитектурное решение отвечало реальным требованиям системы, а не само становилось источником ненужной сложности.
Хотите узнать больше? Изучите другие статьи из раздела: