API Composition¶
API Composition - способ выполнить запрос, которому нужны данные нескольких сервисов: composer вызывает
сервисы-владельцы данных и склеивает их ответы в памяти.
Зачем: у каждого сервиса своя БД, JOIN между ними нет, а фронт не знает топологию сервисов, должен быть один
запрос - один собранный ответ.
Как работает¶
+-------------+ --> order-service (заказ)
GET /order/42 --> | Composer | --> payment-service (оплата)
| | --> shipping-service (доставка)
+-------------+
|
in-memory join --> один ответ клиенту
- Composer получает запрос клиента.
- Вызывает сервисы-владельцы данных. Независимые вызовы идут параллельно. Если второму вызову нужен результат первого (id клиента из заказа), вызовы идут синхронно.
- Склеивает ответы в один объект.
- Если часть сервисов не ответила, отдаёт partial response: что собралось, а недоступное помечает.
Кто бывает composer¶
| Composer | Как | Когда |
|---|---|---|
| API Gateway | Маршрут собирает ответ из нескольких сервисов | Простая склейка, одинаковая для всех клиентов |
| BFF | Сборка под экраны одного клиента | Клиенты разные, у каждого своя форма ответа |
| Отдельный aggregator-сервис | Обычный сервис, который владеет запросом | Сборка сложная или нужна нескольким потребителям |
| Клиент | Фронт сам вызывает сервисы и склеивает ответы | Почти никогда: лишние round-trip, клиент знает топологию сервисов |
Gateway и composition - не одно и то же. Gateway отвечает за вход и роутинг, composition - за сборку ответа. Gateway часто умеет composition, но composer может быть и отдельным сервисом.
Грабли¶
- Доступность падает с каждым вызовом. Composer нужен ответ всех сервисов: три сервиса по 99,9% вместе дают около 99,7%. Спасают partial response и fallback на необязательных частях.
- Латентность. Параллельно - по самому медленному вызову, цепочкой - сумма всех. Ставь таймаут на каждый вызов.
- N+1 по сети. Список из 10 000 заказов, где для каждого дёргается customer-service, так не собрать. Нужен
batch-эндпоинт
GET /customers?ids=...или read model. - Нет согласованного снимка. Каждый сервис отвечает из своей БД в свой момент, общей транзакции нет. Заказ уже отменён, а оплата ещё «прошла» - composer склеит и то и другое.
- Фильтры и сортировка через границы. «Заказы клиентов из Казани по сумме» требуют данных двух сервисов сразу. Composer вытянет всё и отсортирует в памяти, на больших объёмах это не работает.
Когда composition не тянет¶
Тяжёлые запросы, сложные фильтры через границы, чтений намного больше, чем записей - тогда нужен CQRS: отдельная read model, которую заранее собирают из событий сервисов. Чтение из неё - один запрос, цена - eventual consistency. См. Запросы через границы сервисов.
| API Composition | CQRS read model | |
|---|---|---|
| Когда собирается ответ | на лету, при запросе | заранее, из событий сервисов |
| Свежесть данных | всегда актуальные | с лагом, eventual consistency |
| Сложные фильтры и сортировка | плохо, всё в памяти composer | хорошо, схема заточена под запрос |
| Доступность | нужны все вызываемые сервисы | нужна только read model |
| Сложность | низкая | своё хранилище, подписка на события, пересборка |
Итого: composition - самый простой способ собрать ответ из нескольких сервисов. Для простых запросов на разумных объёмах его хватает, тяжёлые чтения переезжают в read model.