Описание интеграции: контракт API глазами аналитика
На стыке двух систем ошибается не код, а договорённость. Каждая сторона делает свою часть правильно — так, как её поняла.
Две команды договорились на встрече: «мы отправляем вам заказ, вы возвращаете статус». Каждая сделала свою часть, обе прошли свои тесты. На стыке выясняется, что «статус» у одних — код, у других — текст, а повторную отправку того же заказа вторая система считает новым заказом и отгружает его дважды.
Ни одна команда не ошиблась в своей части. Ошибка в том, что между ними не было контракта — только устная договорённость о счастливом пути.
Что описывает контракт и чего не описывает спецификация
Машиночитаемое описание API — OpenAPI, схема сообщения — отвечает на вопрос о форме: какие поля, какие типы, какой адрес. Оно молчит о смысле: что значит каждое поле, когда вызывать, что делать, если ответа нет. За форму отвечают разработчики, за смысл — аналитик. Одно без другого не работает: форма без смысла даёт стык, который собирается, но ведёт себя неверно.
Шесть разделов описания интеграции
- Назначение и инициатор. Кто вызывает, по какому событию, ждёт ли ответа сразу или узнаёт результат потом.
- Данные. Поля со смыслом и обязательностью. Если поле есть в модели данных, достаточно ссылки на словарь, а не пересказа.
- Ответы и ошибки. Какие исходы возможны и что вызывающая сторона делает в каждом.
- Повторы и дубли. Что будет, если один и тот же запрос придёт дважды.
- Ограничения. Частота, объём, время ожидания ответа — нефункциональные требования к стыку.
- Версии. Как контракт меняется и сколько живёт старая версия.
К этому — пример запроса и ответа, настоящий, а не схематичный. По примеру обе стороны проверяют себя, и он первым показывает расхождение в понимании.
Ошибки: что делает вызывающая сторона
Список кодов ответа сам по себе ничего не даёт. Полезна колонка справа — решение, что делать с каждым исходом.
| Исход | Пример | Что делает вызывающая сторона |
|---|---|---|
| Неверные данные | Нет обязательного поля | Не повторять. Исправить источник, сообщить тому, кто вводил |
| Нет доступа | Ключ истёк | Не повторять. Поднять тревогу: сам не исправится |
| Уже существует | Заказ с этим номером уже принят | Решить заранее: это успех повтора или конфликт |
| Временная недоступность | Сервис перегружен | Повторить с паузой, растущей от попытки к попытке; после N попыток — в очередь на разбор |
| Результат неизвестен | Запрос ушёл, ответ не пришёл | Самый опасный случай — см. ниже |
Неизвестный результат и идемпотентность
Если ответ не пришёл, вызывающая сторона не знает главного: выполнена операция или нет. Повторить — рискнуть сделать её дважды. Не повторить — рискнуть не сделать вовсе. Для заказа или платежа обе ошибки стоят денег.
Выход — договориться, что повтор безопасен. Операция, которую можно выполнить дважды с тем же результатом, что и один раз, называется идемпотентной. Обычно для этого вызывающая сторона передаёт ключ запроса, а принимающая запоминает его и на повтор с тем же ключом возвращает прежний результат, не выполняя операцию снова.
POST /orders
Idempotency-Key: 7f3c2a90-5d1e-4b8a-9c1f-0e2d4a6b8c10
{ "external_id": "SHOP-118204", "items": [ ... ], "total": 1249.90 }
первый вызов → 201, order_id 55120
повтор → 200, order_id 55120 (заказ не создан повторно)
Аналитику не нужно выбирать механизм. Нужно записать требование: «повторная отправка того же заказа не создаёт второй заказ» — и указать, по какому признаку заказ считается тем же.
Асинхронный стык дублирует по построению. Если обмен идёт через очередь или события, доставка обычно гарантируется «хотя бы один раз», а порядок — не гарантируется вовсе. Получатель должен переносить дубли и сообщения, пришедшие не по порядку. Это требование к получателю, и оно пишется в контракт явно.
Версии и изменения
Контракт будет меняться. Чтобы изменение не ломало соседей, полезно заранее договориться о двух правилах:
- Совместимое изменение — добавить необязательное поле. Безопасно, если получатель игнорирует незнакомые поля; это стоит записать как требование к получателю.
- Несовместимое — переименовать поле, сделать его обязательным, изменить смысл. Такое изменение выпускается новой версией, а старая живёт оговорённый срок.
Опаснее всего изменение смысла без изменения формы: поле называется так же и имеет тот же тип, но «сумма» вдруг стала включать доставку. Схема его не заметит. Заметит только словарь данных — если он есть.
Признак, что контракта нет: о поведении соседней системы узнают из её журналов ошибок.
Проверка за пятнадцать минут
Возьмите одну интеграцию и задайте обеим командам один вопрос: что произойдёт, если запрос ушёл, а ответ не пришёл? Если ответы разные — или их нет, — контракт нужно дописывать с этого места.
Что дальше
Описание готово, разработка идёт — и в середине приходит изменение. Его надо внести так, чтобы оно дошло и до кода, и до контракта, и до соседней команды. Это следующий этап — разработка.