Перейти к содержанию

SOA (Service-Oriented Architecture)

SOA - стиль архитектуры, в котором система собирается из сетевых сервисов с явными контрактами. Каждый сервис отвечает за свою бизнес-функцию, а остальные обращаются к нему только через контракт.

Note

SOA - это подход к проектированию, а не конкретный стек. SOAP, WS-* и ESB - самая известная его реализация, но не определение. REST-сервисы, нарезанные по бизнес-функциям, - тоже SOA.

Термин ввели аналитики Gartner Yefim Natis и Roy Schulte (1996). SOA выросла из задачи интеграции: как связать между собой уже работающие системы на разных технологиях. Вопрос «как построить новое приложение из сервисов» появился позже.

Что считается сервисом

Сервис в SOA - всё, у чего есть сетевой контракт:

  • веб-сервис
  • обёртка над легаси-системой или мейнфреймом
  • промышленный контроллер или датчик
  • покупная система (CRM, ERP) со своим API

В микросервисах сервис почти всегда - отдельно деплоящееся приложение со своей БД. В SOA понятие шире.

Зачем нужна SOA

  • Много команд. Они мешают друг другу в общем репозитории и общем релизном цикле. Самая частая причина.
  • Независимые релизы. Изменение в одном сервисе выкатывается, не затрагивая остальные.
  • Зоопарк систем. Легаси, покупные системы и новый код общаются через единые контракты. Исторически главная причина SOA в enterprise.
  • Разная нагрузка на части. Поиск нагружен в сто раз сильнее биллинга, и масштабировать его нужно отдельно.
  • Разные технологии. Одному сервису нужна Java, другому Python с ML-библиотеками.
  • Изоляция отказов. Упали отчёты - приём заказов продолжает работать.
  • Переиспользование. Один сервис аутентификации или один CustomerService на всю компанию.

Когда SOA не нужна

Нужна только горизонтальная масштабируемость. Распределённость - свойство развёртывания, а не архитектуры. Монолит тоже масштабируется: N копий одного артефакта за балансировщиком. Java EE / Jakarta EE даёт распределённость на уровне сервера приложений: кластер WildFly решает задачу без переделки кода.

Note

С лекции: если ты не опытный архитектор, кластер на WildFly лучше самодельных микросервисов.

Одна небольшая команда. Закон Конвея: структура системы повторяет структуру общения в организации. Команде из пяти человек сервисы дадут все проблемы распределённой системы и ни одного преимущества.

Принципы

  • Слабая связанность - сервисы минимально зависят друг от друга и обращаются друг к другу редко.
  • Явный контракт - интерфейс сервиса описан формально (WSDL, OpenAPI), протоколы стандартные.
  • Автономность - сервис сам выполняет свою функцию и владеет своими данными, никто не ходит к нему в базу напрямую. Если соседи упали, он продолжает работать хотя бы частично. Отсюда таймауты, circuit breaker и кеши.
  • Stateless - сервис не хранит состояние между запросами, поэтому любой запрос может обработать любой экземпляр.
  • Прозрачность размещения - потребителю не важно, где физически стоит сервис. Он обращается по имени (URI), а конкретный адрес (URL) находится через реестр.
  • Нормализация - у каждой бизнес-функции один сервис-владелец, функции не дублируются.

Как резать на сервисы

Границы проводят по бизнес-подобластям - bounded context из DDD: у каждой своя модель и свой язык. Хорошая граница там, где связей между частями мало и они редкие.

Это аналитическая работа, и сделать её хорошо сложно. Ошибиться можно в обе стороны:

  • Слишком крупные сервисы - по сути тот же монолит, только с сетью. Делить не было смысла.
  • Слишком мелкие - сервисы постоянно вызывают друг друга. Сеть - самая медленная часть системы, и производительность падает.

Правильный размер - всегда компромисс между этими крайностями. Для некоторых систем хорошей нарезки нет вовсе.

Как устроено: поставщик, реестр, потребитель

Классическая модель SOA состоит из трёх ролей:

                 ┌──────────────┐
     publish     │    Реестр    │     find
  ┌─────────────▶│  (registry)  │◀─────────────┐
  │              └──────────────┘              │
┌─┴───────────┐                        ┌───────┴─────┐
│  Поставщик  │◀──────── bind ─────────│ Потребитель │
│ (provider)  │   вызов по контракту   │ (consumer)  │
└─────────────┘                        └─────────────┘
  1. Поставщик публикует описание своего сервиса в реестре (publish).
  2. Потребитель ищет в реестре сервис по нужным критериям (find).
  3. Потребитель получает контракт и адрес и вызывает сервис напрямую (bind).

В 2000-х реестр задумывали глобальным: стандарт UDDI описывал каталог, где компании публикуют свои сервисы и находят чужие. Это не прижилось, публичные реестры закрылись примерно к 2006 году.

Внутри компаний идея осталась:

  • service discovery - Consul, etcd, Eureka, DNS и Endpoints в Kubernetes: найти адрес сервиса по имени
  • каталоги контрактов - Schema Registry для Kafka, внутренние API-порталы, Backstage

Классическая SOA: ESB, SOAP, WS-*

Обычно различают два вида SOA:

  • классическая (enterprise) - общая шина ESB, централизованная оркестрация, SOAP и стандарты WS-*
  • лёгкая - сервисы общаются напрямую по HTTP/REST или через простой брокер сообщений

ESB (Enterprise Service Bus)

ESB - центральная шина интеграции. Сервисы не вызывают друг друга напрямую, все сообщения идут через шину.

        ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
        │ Billing  │   │   CRM    │   │ Inventory│   │  Legacy  │
        │ service  │   │ service  │   │ service  │   │ mainframe│
        └────┬─────┘   └────┬─────┘   └────┬─────┘   └────┬─────┘
             │              │              │              │
             ▼              ▼              ▼              ▼
        ════════════════════════════════════════════════════════
        ║         ESB (routing, transform, orchestration)       ║
        ════════════════════════════════════════════════════════
             ▲              ▲              ▲
             │              │              │
        ┌────┴─────┐   ┌────┴─────┐   ┌────┴─────┐
        │  Orders  │   │ Shipping │   │ Partner  │
        │ service  │   │ service  │   │ gateway  │
        └──────────┘   └──────────┘   └──────────┘

Что делает шина:

  • routing - решает, куда доставить сообщение (а не отправитель)
  • transformation - на лету переводит формат одного сервиса в формат другого
  • protocol bridging - связывает сервисы на разных протоколах, например SOAP и JMS
  • orchestration - собирает несколько вызовов в один бизнес-процесс, часто на BPEL

Вся логика интеграции живёт в шине, а сервисы на концах простые. Это называют smart pipes, dumb endpoints. В микросервисах наоборот: dumb pipes, smart endpoints.

Минусы ESB:

  • Упала шина - встала вся система.
  • Шина ограничивает производительность всех сервисов сразу.
  • Любое изменение интеграции делает одна команда шины, остальные ждут в очереди. Независимых релизов, ради которых делили систему, больше нет.
  • Логика размазана между сервисами и шиной, отлаживать тяжело.
  • ESB-продукты дорогие и проприетарные (vendor lock-in).

Для небольшой системы ESB избыточна: сервисы спокойно общаются напрямую.

SOAP и WSDL

SOAP - протокол обмена XML-сообщениями. Сообщение - это конверт (Envelope) со служебными заголовками (Header) и телом (Body). Обычно отправляется POST'ом на один URL поверх HTTP, но может идти и через JMS.

WSDL - XML-контракт сервиса: какие есть операции, какие данные они принимают и возвращают, по какому адресу их вызывать. По WSDL генерируется код клиента и сервера.

WS-*

WS-* - набор стандартов поверх SOAP. Каждый добавляет свой блок в Header и закрывает то, чего нет в самом SOAP:

  • WS-Security - подпись и шифрование самого сообщения
  • WS-Addressing - ID сообщения и адрес для ответа, асинхронный обмен
  • WS-ReliableMessaging - гарантированная доставка
  • WS-AtomicTransaction - распределённые транзакции (2PC)

Плюсы и минусы

Плюсы - это причины из раздела «Зачем нужна SOA», когда они действительно сработали. Сверх них:

  • Единые контракты - разнородные системы интегрируются по одним правилам.
  • Централизованные стандарты безопасности, логирования и мониторинга. В большой регулируемой организации это скорее плюс.
  • Нарезка по бизнесу как явный принцип - задолго до того, как DDD стал мейнстримом.

Минусы:

  • Сложнее архитектура - сеть, контракты, отдельное развёртывание каждого сервиса.
  • Нет общих транзакций - ACID между сервисами невозможен, нужны Saga, Outbox и eventual consistency.
  • Сложнее отладка - стектрейс через сеть не проходит, нужен распределённый трейсинг.
  • Сложнее тестировать взаимодействие сервисов.
  • Больше эксплуатации - service discovery, централизованные логи, CI/CD на каждый сервис.
  • Версионирование контрактов - сигнатуру нельзя просто поменять, у сервиса есть живые потребители.
  • Никто не видит систему целиком - нужны документация и ADR.
  • Общая БД (частая в SOA) - скрытая связанность: изменил схему ради одного сервиса, сломал три других.
  • Тяжёлый стек - SOAP и WS-* многословны, инструменты громоздкие, порог входа высокий.
  • Минусы ESB - см. ESB.

SOA и микросервисы

Ось SOA Микросервисы
Размер сервиса крупные, вокруг бизнес-домена мелкие, вокруг одной capability
Коммуникация через ESB, smart pipes dumb pipes, smart endpoints
Данные часто общая БД / общая модель у каждого сервиса своя БД
Координация централизованная оркестрация в ESB хореография или лёгкая оркестрация
Управление governance сверху, единые стандарты автономия команд
Протоколы SOAP / WS-*, тяжёлые контракты HTTP/REST, gRPC, лёгкие контракты

Микросервисы можно считать развитием SOA: та же идея сервисов вокруг бизнес-функций, но без центральной шины и общей базы (см. Микросервисы).


TODO

  • Компенсация распределённости: таймауты, retry с backoff, идемпотентность, circuit breaker.
  • Согласованность: почему 2PC плохо работает между сервисами и что вместо него.
  • Strangler Fig как практический путь от монолита к сервисам.
  • Fallacies of Distributed Computing - хорошо подытоживают всё про сеть.