Архитектура взаимодействия сервисов и интеграционные паттерны

опубликовано: ⏰ 17

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

Введение

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

За последние пару десятилетий подходы заметно изменились, от монолитных приложений до сложных распределенных ландшафтов с размещением на облачных платформах (как on-premises, так и cloud-based) с сотнями или тысячами микросервисов. Вместе с этим изменились и шаблоны интеграции: от простого и старого точечного взаимодействия (P2P) до сложных сервисных сеток (Service Mesh) и событийно-ориентированных топологий (EDA).

Подходы организации взаимодействия

В целом, все подходы хотел бы разделить на 3 категории:

  1. Централизованные или монолитные с единой точкой интеграции:
    1. Корпоративная шина данных (ESB).
    2. Интеграционная платформа как услуга (iPaaS).
  2. Децентрализованные с синхронным взаимодействием:
    1. Точечное взаимодействие (Point-to-Point / P2P).
    2. API-шлюз с возможностью агрегации запросов (API Gateway).
    3. Управляющий инфраструктурный слой (Service Mesh).
    4. Интеграционные микросервисы (Integration Microservices / Smart Endpoints)
  3. С асинхронным взаимодействием через брокер сообщений
    1. Архитектура на основе брокеров сообщений (Message Broker Architecture).
    2. Событийно-ориентированная архитектура (EDA).

Точечное взаимодействие (Point-to-Point / P2P)

Это наверное самый очевидный и простой способ организации взаимодействия. Чаще всего с него начинается разработка и хорошо подходит для небольших систем. В этом подходе сервисы общаются напрямую: Сервис А знает сетевой адрес Сервиса Б и вызывает его по протоколу HTTP (REST), gRPC, SOAP и так далее.

Подход

При P2P взаимодействии каждый сервис выступает и в роли клиента, и в роли сервера. Зависимости прописываются жестко: в конфигурационных файлах или через механизмы реестры сервисов (Service Discovery). Если Сервису А нужны данные от Сервиса Б и Сервиса В, он делает два последовательных вызова.

Преимущества

  1. Простота старта, так как нет необходимости внедрять сложную инфраструктуру (брокеры сообщений, шины). Это простейший путь развития.
  2. Высокая производительность, так как прямой вызов теоретически дает минимальную задержку, так как отсутствуют промежуточные звенья.
  3. Прозрачность и простота отладки из-за того, что легко трассировать запрос, видно, кто кого вызвал.

Недостатки и риски

Главная проблема P2P – это экспоненциальный рост сложности связей. Если у нас есть N сервисов, и каждый должен общаться с каждым, количество связей стремится к N * (N - 1) / 2.

  • Сервис-потребитель жестко привязан к интерфейсу (контракту) провайдера, что означает высокую связанность. Любое изменение API провайдера требует обновления всех потребителей. Это создает “хрупкую” архитектуру.
  • Из-за жестких связей сервисы становится невозможно деплоить независимо. Вы должны выпускать новые версии всех связанных сервисов одновременно или вводить правила версионирования с поддержкой обратной совместимости.
  • Часто разным сервисам требуются схожие преобразования данных или проверки прав доступа, что ведёт к дублированию логики. В P2P архитектуре эта логика дублируется в каждом сервисе-клиенте.

Итог

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

Корпоративная шина данных

Как альтернатива P2P, в начале 2000-х годов доминирующей парадигмой стала сервис-ориентированная архитектура (SOA) с использованием корпоративной шины (ESB). Идея заключалась в централизации всей логики интеграции.

Подход

ESB – это готовое решение (например, ServiceMix, WSO2, MuleSoft, IBM Integration Bus, Oracle Service Bus), через который проходят все сообщения между сервисами. Сервисы подключаются к шине, а не друг к другу. Шина берет на себя функции:

  • Маршрутизации (отправка сообщения нужному получателю).
  • Трансформации (преобразование данных из формата отправителя в формат получателя).
  • Оркестрации (управление последовательностью вызовов).
  • Мониторинга и безопасности.
  • Готовые коннекторы к внешним системам, такие как базы, CRM, ERP и прочие.

Ключевой принцип организации, то приведение к единой модели данных внутри ESB. Сервисы говорят на своих “диалектах”, но внутри шины данные приводятся к единому стандарту. Это позволяет добавлять новые сервисы, не ломая интеграцию со старыми.

Преимущества

  • Сервисы знают только о шине, а значит обеспечивается слабое связывание. Изменение одного сервиса требует изменения только адаптера на шине.
  • ESB позволяет организовать централизованное управление. Безопасность, логирование и мониторинг всех потоков данных в одном месте. Это значительно облегчает работу отдела эксплуатации.
  • Логика трансформации и бизнес-правила пишутся один раз и используются многократно, что позволяет не дублировать функционал и сэкономить на разработке.
  • Зачастую поставщики решений предоставляют неплохую техническую поддержку. В том числе регулярные обновления с исправлениями ошибок и известных (zero-day) уязвимостей.

Недостатки и риски

  • Фактически, это единая точка отказа. Если шина падает, останавливается вся интеграция. Хотя горизонтальное масштабирование поддерживается, но масштабировать монолитную ESB сложнее и дороже, чем stateless-сервисы.
  • ESB решения часто проприетарны и дороги как в лицензировании, так и в поддержке (требуют дорогих специалистов). Что означает высокую зависимость от поставщика решения.
  • Есть риск ухода поставщика с рынка (например, как результат введения санкций). Довольно неприятная ситуация, в которой вы останетесь с неподдерживаемым и не обновляемым решением требующее срочной замены.
  • Проприетарные решения являются “черной коробкой”, требуются специалисты с высоким уровнем компетенций для доработок (если это возможно). Наработка релевантного опыта потребует годы.

Итог

ESB все еще актуальна для крупных, консервативных корпораций (банки, телеком) с огромным количеством легаси-систем. Однако для простых проектов] и облачных продуктов классическая тяжелая ESB может стать проблемным не гибким решением. Но может быть и наоборот, сильно сэкономить время на разработку, так как предоставляет уже разработанную и отлаженную поставщиком полноценную экосистему (см. выше функции ESB).

Архитектура на основе брокеров сообщений

Альтернатива ESB – использование брокеров сообщений, но с иным подходом: “dump pipes, smart endpoints”. Логика возвращается в сервисы, а брокер занимается только доставкой. На самом деле, часто слышал как этот подход называют “нашей” шиной, что не совсем верно.

Подход

Сервисы обмениваются сообщениями через очереди (queues) или топики (topics). Это асинхронный подход: отправитель отправляет сообщение и сразу продолжает работу, не дожидаясь ответа.

  • В очередях сообщение потребляется одним получателем (используется для команд).
  • В топиках сообщение получают все подписчики (используется для событий).

Некоторые примеры:

  • RabbitMQ, ActiveMQ Artemis – классические брокеры с возможностью маршрутизации сообщений. Отлично подходит для задач, где важна гарантия доставки и сложная логика маршрутизации.
  • Apache Kafka, Redpanda, Aapche Pulsar – платформы потоковой передачи данных. По сути, это не просто брокер, а распределенный лог. Идеален для Event Streaming.

Преимущества

  • Отправитель и получатель не должны быть доступны одновременно. Система работает, даже если принимающий сервис недоступен (на обслуживании, авария).
  • Брокер может и выступает неким буфером. Если пришло 1000 запросов в секунду, а сервис обрабатывает только 100, сообщения встанут в очередь, и сервис не упадет.
  • Потребители можно горизонтально масштабировать, добавляя экземпляры, читающие из одной очереди.

Недостатки и риски

  • Асинхронность делает трассировку и отладку сложной. Без сторонних инструментов не видно напрямую, где застряло сообщение и его содержимое.
  • Могут быть проблемы с гарантией строгого порядка сообщений при параллельной обработке.
  • Предварительно необходимо предусмотреть, что делать с сообщениями, которые не могут быть обработаны (DLQ), иначе очередь переполнится.
  • Возможны переполнения очередей с медленными потребителями, что может привести неработоспособности брокера в целом.
  • При кластерном развёртывании необходимо учитывать, что сообщения могут быть потеряны при сбое брокера. Требуется проработка механизмов надёжной репликации между узлами и предварительное тестирование на инфраструктуре развёртывания.
  • Брокер это единая точка отказа, хотя об это редко кто упоминает. Недоступность брокера заблокирует всю работу сервисов и может привести к потере данных, если такая ситуация не была учтена со стороны сервисов.

Итог

Брокеры сообщений, преимущественно, используются при организации взаимодействия в решениях с большим количеством сервисов и высокой нагрузкой (например, MSA). Это один из обязательных элементов для построения надежных корпоративных-систем для обеспечения надёжности и отказоустойчивости систем.

Событийно-ориентированная архитектура

Событийно-ориентированная архитектура (EDA – Event-Driven Architecture) – это дальнейшее развитие асинхронного взаимодействия через брокеры сообщений. Если в брокерах мы часто используем сообщения как “команды” (сделай что-то), то в EDA основным объектом является “событие” (что-то случилось).

Подход

Сервис не даёт команду другому сервису, что делать. Он только сообщает – “произошло событие X”. Основные принципы EDA:

  1. Хранение состояния системы как последовательности событий (Event Sourcing), а не текущего состояния в БД.
  2. Разделение модели записи и модели чтения (CQRS). EDA хорошо ложится на CQRS – события используются читателями.

Пример:

  • Команда (Message Broker) – сервис заказов отправляет команду в сервис склада: “Отгрузи товар”.
  • Событие (EDA) – сервис заказов публикует событие: “Заказ создан”. Сервис склада слушает это событие и решает отгрузить товар. Сервис уведомлений слушает то же событие и отправляет SMS.

Преимущества

  • Можно добавить нового подписчика (например, аналитику) на событие “Заказ создан”, вообще не трогая сервис заказов. Это принцип open/closed на уровне архитектуры и даёт максимальную гибкость при расширении систем.
  • Система реагирует на изменения сразу же при его возникновении. Это не системы реального времени, но всё же реакция довольно быстрая по сравнению с обработчиками основанными на вызовах по планировщикам.

Недостатки и риски

  • В EDA данные не согласованы в данный момент времени (Eventual Consistency). Если мы изменили данные, подписчик узнает об этом через миллисекунды или секунды. Это необходимо учитывать, например при дизайне пользовательского интерфейса.
  • Формат события должен быть формализован и согласован. Изменить структуру события сложнее, чем в синхронном API, так как потребителей может быть неизвестное количество.

Итог

EDA – это выбор для высоконагруженных, масштабируемых систем, требующих гибкости. Однако это требует высокой культуры разработки и понимания принципов распределенных транзакций (Saga). Нет смысла использовать EDA везде без нужды – сложность поддержки довольно высока.

API-шлюз с возможностью агрегации запросов

Когда количество микросервисов переваливает за десятки, проблемой становится не только взаимодействие сервисов, но и взаимодействие внешних клиентов (мобильных приложений, фронтендов). Клиенту нужно сделать 10 запросов к 10 разным сервисам, чтобы отрисовать страницу. Одно из решений – API Gateway.

Подход

API Gateway — это единая точка входа. Он выполняет роль “обратного прокси” с внутренней логикой маршрутизации и агрегации запросов. Основные функции:

  1. Маршрутизация – перенаправление запросов /api/users -> User Service, /api/orders -> Order Service.
  2. Аутентификация и авторизация – проверка JWT-токена происходит на шлюзе. Внутренние сервисы получают уже авторизованный запрос, что снимает с них нагрузку.
  3. Агрегация – шлюз может вызвать 3 сервиса, собрать ответ в один JSON и отдать клиенту (Mobile BFF).
  4. Применение лимитов по запросам для защиты от DDoS и перегрузки бэкенда.
  5. Дополнительно может выполнять балансировку запросов.

В какой-то степени сюда можно отнести и GraphQL – он тоже может являться шлюзом для получения данных из нескольких сервисов/микросервисов.

Преимущества

  • Скрытие сложной топологии внутренних сервисов. Клиент может общаться только с одним эндпоинтом, а не с отдельными сервисами.
  • Возможность централизованно обеспечивать требования по безопасности (аутентификация, аудит).
  • В некоторых случаях, возможность снизить нагрузку на бекенд выполняя проверку токенов и обработку HTTPS трафика только на шлюзе, передавая далее уже авторизованный запрос по HTTP.
  • Позволяет ограничивать количество запросов (rate limiting, throttling) и исключать недоступные сервисы из маршрутизации (circuit breaker).

Недостатки и риски

  • Это единая точка отказа, поэтому необходимо разворачивать в высокодоступной конфигурации и не хранить состояние на шлюзах.
  • Может стать узким местом в производительности, так как весь трафик будет проходить через шлюз. А агрегация запросов может ещё сильнее усугубить ситуацию.
  • Возможны сложности с конфигурированием при большом количестве сервисов и их версионированием.
  • Есть определённый риск избыточного усложнения логики шлюза. Она должна оставаться максимально простой и не превращаться в аналог ESB с условиями, маршрутизацией и трансформацией запросов.
  • Шлюз привносит дополнительную задержку, что может быть критично в некоторых случаях.

Backend for Frontend (BFF)

Backend for Frontend (BFF) – развитием идеи API Gateway. Проблема универсального шлюза в том, что требования разных клиентов различаются. Мобильному приложению нужно меньше данных, чем веб-версии, а IoT-устройству нужен совсем другой формат. Мы создаем не один шлюз, а например отдельные сервисы для каждого фронтенда:

  • Web-BFF: Для десктопной версии. Возвращает полные данные, богатый HTML/JSON.
  • Mobile-BFF: Для мобильного приложения. Возвращает обрезанные данные, экономит трафик, агрегирует вызовы для снижения latency.

Преимущества:

  • Устранение «Fat Client» — логика того, какие данные нужны, переходит с клиента на бэкенд.
  • Упрощение самого клиента.
  • Независимость релизных циклов.

Недостатки и риски:

  • Дублирование кода между разными BFF. Решается выносом общей логики в нижележащие сервисы или библиотеки.

Итог

Если сравнивать с ESB, то ESB связывает сервисы между собой внутри контура системы (System-to-System). API Gateway связывает внешний мир с системой (Client-to-System). API Gateway обычно синхронный и не занимается сложными трансформациями бизнес-логики.

API Gateway обязателен для любой микросервисной архитектуры, открытой во внешний мир. Это основа для организации безопасного, предсказуемого и управляемого API.

Интеграционные микросервисы

Можно сказать, что это противоположность ESB. Интеграционные микросервисы, это сервисы чья единственная ответственность – обеспечение взаимодействия. Они реализуют принцип “smart endpoints, dumb pipes”.

Подход

В этом подходе логика интеграции не выносится в отдельное решение (как ESB), а инкапсулируется в коде специальных микросервисов. Существует два основных типа таких сервисов:

  • Адаптеры (Anti-Corruption Layer) – сервисы, которые оборачивают внешние или легаси-системы. Они переводят входящие запросы в понятный внутренний формат и обратно. Пример: У нас есть старая ERP-система, работающая по протоколу SOAP с громоздкими XML. Мы пишем микросервис ErpIntegrationService, который выставляет современный REST/gRPC интерфейс, а внутри преобразует вызовы в SOAP.
  • Процессоры-оркестраторы – сервисы, которые управляют сложными транзакциями (Saga) или агрегируют данные из нескольких источников, не перекладывая эту ответственность на клиента или шлюз.

Преимущества

  • В отличие от ESB, где вы ограничены языком и инструментами шины, интеграционный микросервис можно написать на любом языке. Если нужно парсить тяжелый XML – пишем на Java (JAXB). Если нужно работать с высоконагруженным потоком – пишем на Go или Rust. Команды разработчиков полностью владеют стеком технологий.
  • Интеграционные сервисы масштабируются независимо. Если интеграция с внешним сервисом “тормозит” и требует 100 инстансов для обработки очереди, а внутренняя интеграция работает на одном инстансе, вы можете выделить ресурсы точечно. В ESB же масштабируется весь монолит шины, что дорого и затратно.
  • Интеграционная логика часто бывает грязной (обработка ошибок сторонних API, ретраи, специфические протоколы). Выделение этой логики в отдельный сервис позволяет изолировать сложность в одном месте, не загрязняя код бизнес-сервисов. Это реализация паттерна Anti-Corruption Layer из Domain-Driven Design (DDD).
  • Падение одного интеграционного сервиса (например, коннектора к SMS-шлюзу) не ломает всю инфраструктуру связи, как это было бы с ESB. Остальные части системы продолжают работать.

Недостатки и риски

  • Без жесткого централизованного управления команды часто пишут одни и те же клиенты к API по несколько раз. В каждой команде появляется свой “велосипед” для вызова сервиса доставки или оплаты. Необходима развитая культура разработки общих библиотек, чтобы переиспользовать код клиентов и сервисов.
  • Если интеграционный сервис содержит слишком много бизнес-логики и жестко привязан к структуре данных других сервисов, он становится узким местом. Частое изменение контрактов приводит к каскадным изменениям в интеграционных сервисах. Вы получаете все минусы распределенных систем (сетевые задержки, сложность отладки) и минусы монолита (сильную связность).
  • В ESB весь мониторинг собирается централизованно и лежит в одном месте. В архитектуре с интеграционными микросервисами цепочка вызовов может быть длинной: Client -> Gateway -> OrderService -> IntegrationService -> ExternalAPI. Отладка ошибки, произошедшей на 4-м шаге, требует отличной системы распределенной трассировки. Без нее поиск причины сбоя превращается в ад.
  • Интеграционный сервис, вызывающий три внешние системы, должен сам заботиться о целостности данных. Если вторая система ответила ошибкой, нужно откатить изменения в первой. Это требует реализации паттернов компенсирующих транзакций (Saga) в коде сервиса, что усложняет разработку.

Итог

Интеграционные микросервисы – это удобный подход для построения интеграционных взаимодействий и как “дешёвая” альтернатива ESB:

  • Используйте их для реализации Anti-Corruption Layer при работе с легаси.
  • Используйте их для выноса сложной логики оркестрации за пределы доменных сервисов.

Стоит отметить, что не стоит создавать “божественные сервисы”, который замыкают на себе большое количество потоков данных. Дробите интеграционные сервисы по границам контекстов (Bounded Context) так же, как вы это делаете с бизнес-сервисами.

Управляющий инфраструктурный слой

С ростом числа микросервисов вопросы надежности (retries, timeouts, circuit breaking, mTLS) стали выходить на передний план. Реализовывать эту логику в коде каждого сервиса проблематично и избыточно. Решение проблемы на инфраструктурном уровне – Service Mesh.

Подход

Service Mesh (на примере Istio, Linkerd) внедряет рядом с каждым сервисом специальный прокси-контейнер (Sidecar).

  • Data Plane – сами прокси, которые перехватывают весь входящий и исходящий трафик: шифруют (mTLS), маршрутизируют, балансируют, повторяют, обеспечивают наблюдаемость.
  • Control Plane – управляющий слой, который говорит прокси, как себя вести: конфигурация правил, реестры сервисов, управление сертификатами, сбор телеметрии.

Преимущества

  • Неважно, на каком языке написан сервис. Логику сети (retries, security) берет на себя Sidecar.
  • Автоматические метрики, трассировка для всего трафика без изменения кода приложения.
  • Взаимная аутентификация сервисов (mTLS) настраивается одной политикой.
  • Возможность встроить legacy-сервисы в текущую инфраструктуру без их доработки.

Недостатки и риски

  • Service Mesh – это довольно сложно в настройке и поддержке. Добавляется лишний слой абстракции.
  • Sidecar-прокси потребляют ресурсы (CPU/RAM) и добавляют задержку (hop).

Итог

Не стоит использовать Service Mesh, если у вас меньше 50-100 сервисов. Это “тяжелая артиллерия”, которая принесет больше проблем, чем пользы на малых масштабах.

Интеграционная платформа как услуга

Здесь мало что могу сказать, никогда не приходилось использовать на практике. Видимо подходит для компаний, чей IT-ландшафт состоит из множества SaaS-продуктов (Salesforce, Shopify, Slack, Jira) и минимального количества кастомной разработки.

Подход

Это облачные платформы (Zapier, MuleSoft Anypoint, Workato, Azure Logic Apps), предоставляющие готовые коннекторы (“кнопки”) для приложений. Интеграция настраивается через UI (Low-code/No-code).

Преимущества

  • Скорость настройки: дни вместо месяцев.
  • Не нужно поддерживать сервера (Zero Ops).
  • Огромная библиотека готовых коннекторов.

Недостатки и риски

  • Ограниченная кастомизация сложной бизнес-логики.
  • Vendor Lock-in.
  • Gодписка может быть очень дорогой при больших объемах данных.

Итог

iPaaS подходит для интеграции SaaS, бизнес-процессов уровня бизнеса (HR, Маркетинг), но не подходит для высоконагруженного бэкенда с кастомной логикой.

Сравнительная таблица

Итоговое сравнение основных подходов по ключевым критериям.

КритерийP2PESBMessage Broker / EDAAPI Gateway / BFFService MeshИнт. микросервисы
СвязанностьОчень высокаяНизкая (через шину)Низкая (через события)СредняяНизкая (инфраструктурная)Средняя
Тип взаимодействияСинхронныйЛюбойАсинхронныйСинхронный
(преимущественно)
Любой (L7)Любой
Точка отказаОтсутствует
(распределена)
ШинаБрокер
(кластер)
ШлюзControl PlaneРаспределена
Сложность разработкиНизкаяВысокаяВысокая (асинхронность)СредняяНизкая (разработка) / Высокая (devops)Средняя
Простота отладкиВысокаяНизкаяНизкаяСредняяВысокаяСредняя
МасштабируемостьСложноСложноОтличноХорошоОтличноОтлично
ПрименениеСтартапы, простые системыLegacy EnterpriseВысоконагруженные системыВнешние клиентыКрупные Kubernetes-кластерыИнтеграция Legacy, Anti-Corruption Layer

Заключение

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

  • Service Mesh для надежного взаимодействия внутри кластера.
  • Вариации API Gateway для внешнего доступа.
  • Apache Kafka для событийного ядра решения.
  • Redis/RabbitMQ/Artems для фоновых задач и взаимодействия между сервисами.
Interoperability Essentials