Когда API есть, а интеграции нет: 8 правил данных до старта разработки

Когда API есть, а интеграции нет: 8 правил данных до старта разработки

В карточке заказа уже стоит «оплачен». В системе для работы с клиентами (CRM) он ещё ждёт подтверждения. Склад видит заказ как отменённый, потому что получил более раннее событие позже нового. Все три системы ответили на запросы с 200 OK, а оператору всё равно приходится выяснять, кому верить.

Такой сбой редко начинается с недоступного API. Чаще две стороны договорились об адресе запроса, токене доступа и формате JSON, но не о том, что именно считается заказом, в какой момент он оплачен и какая система вправе изменить его статус.

Привет, я Антон Фокин — CEO Qtim. Мы начинаем интеграционные задачи с правил данных. Точки доступа API и очереди становятся понятнее, когда у каждой сущности есть владелец, у каждого изменения — причина, а у каждого повтора — ожидаемый результат.

В такой ситуации мы до старта собираем таблицу решений по одной критичной сущности: заказу, клиенту, счёту, заявке или поставке. По ней мы проводим первую встречу бизнеса, аналитика и разработки. Полное ТЗ она не заменяет, зато помогает заметить разрывы до того, как они окажутся в коде.

API передаёт значение. Интеграция должна сохранить его смысл

Спецификация OpenAPI версии 3.1.2 описывает структуру запроса и ответа. Схема не объясняет, что означает status: "paid": деньги списаны, платёжный провайдер подтвердил операцию, бухгалтерия провела её или менеджер подтвердил оплату.

Та же разница возникает с полями. null, отсутствие поля, пустая строка и значение по умолчанию могут означать четыре разных состояния. Например, отсутствие manager_id иногда означает «значение не меняли», null — «назначение сняли», а пустая строка — ошибку импорта. Пока это не описано, две команды будут писать логичный для себя код и получать разные результаты.

OpenAPI 3.1.2 опирается на JSON Schema и требует явно ограничивать ожидаемый тип. Часть правил сериализации задаёт само приложение. Поэтому рядом со схемой мы фиксируем семантику данных: что принимаем, что возвращаем и как трактуем пограничные значения.

Восемь решений до первой задачи в разработке

Мы начинаем с одной сущности и записываем ответы по сценарию, где ошибка заметнее всего: оплате, отмене заказа, выдаче доступа или смене адреса доставки.

Когда API есть, а интеграции нет: 8 правил данных до старта разработки

Пока решения не записаны, у владельца процесса, аналитика и разработки остаются разные версии одной реальности.

Сущность и владелец данных

Когда API есть, а интеграции нет: 8 правил данных до старта разработки

У сущности почти всегда больше одного идентификатора. UUID — технический уникальный ID записи внутри сервиса. Номер заказа нужен оператору. Идентификатор контрагента приходит из ERP, системы учёта ресурсов компании. Внешний платёжный ID нужен для сверки. Проблема начинается, когда один из них используют как универсальный ключ без правила, кто его создаёт и может ли он измениться.

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

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

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

Поля, которые нельзя трактовать по-разному

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

Для каждой важной характеристики мы записываем пять вещей:

  • формат и единицы измерения;
  • обязательность при создании и изменении;
  • поведение при отсутствии, null и пустом значении;
  • владельца и разрешённый способ изменения;
  • пример корректного и некорректного значения.

Время требует отдельной дисциплины. Запись 2026-10-02 15:00 без часового пояса для одной системы может быть московским временем, для другой — UTC, а для третьей — временем пользователя. Мы фиксируем формат, часовой пояс и то, описывает ли значение момент, дедлайн или локальный слот в расписании.

То же относится к деньгам. «Сумма 1000» без валюты, округления и состава налога не даёт возможности корректно провести сверку. Конкретные правила зависят от предметной области, но договорённость должна появиться раньше сериализатора.

Статусы и момент фиксации

Когда API есть, а интеграции нет: 8 правил данных до старта разработки

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

Здесь мы выбираем момент истины. Платёжный провайдер прислал успешное уведомление — достаточно ли этого для статуса «оплачен»? Или статус меняется после внутренней проверки подписи, суммы и валюты? Одно короткое правило избавляет от разной трактовки между оплатой, складом и поддержкой.

События, порядок и повторы

Интеграция через API нередко начинается как запрос «дай актуальное состояние». Затем появляются вебхуки, очередь, повторная доставка и потребность восстановить пропущенные изменения. В этот момент надо решить, что передаём: снимок сущности или отдельное событие.

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

В контракт обмена мы добавляем идентификатор события, время возникновения, версию сущности или последовательность, правило удаления дублей и срок повторной доставки. Получатель должен понимать, какое сообщение считать устаревшим и что делать с повтором. Без этих полей обработчик потеряет изменение или применит его дважды.

Повтор запроса

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

AWS рекомендует для изменяющих операций ключ идемпотентности (idempotency token): повтор с тем же ключом возвращает тот же результат и не создаёт новый побочный эффект. До старта мы определяем сам ключ, область уникальности, время хранения и ответ для повтора с другим телом запроса.

Ещё один вопрос — конфликт изменений. Два менеджера открыли одну заявку, каждый отредактировал её и нажал «сохранить». До разработки мы договариваемся, как сервис обнаруживает работу со старой копией и какой ответ получает пользователь. При конфликте сервис возвращает понятный ответ и следующий шаг вместо молчаливой перезаписи.

Доступ и границы данных

Когда API есть, а интеграции нет: 8 правил данных до старта разработки

Роли отвечают на вопрос «кто этот пользователь». Интеграции требуют следующего уровня: к какой организации, заказу, складу или документу у него есть доступ и какие поля он вправе увидеть или изменить.

В контракте мы фиксируем tenant — отдельную организацию. Отдельно указываем субъекта доступа, объект, операцию и поле. Для счёта это может выглядеть так: бухгалтер организации видит сумму и реквизиты своего юридического лица, партнёр получает только статус оплаты, а оператор поддержки видит данные по обращению после проверки основания. Такая матрица нужна API, интерфейсу и журналу аудита.

Главное возражение здесь звучит разумно: «У нас уже есть роли». Роли остаются, но без привязки к объекту они не отвечают, какой именно счёт можно открыть по переданному ID.

Пагинация, ошибки и версии

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

AIP-158 относит добавление пагинации в существующий метод к обратно несовместимым изменениям. Поэтому размер страницы, токен продолжения, сортировка и реакция на смену фильтра появляются в контракте с первой версии.

Для потребителя должен быть заранее понятен ответ API на ожидаемую ошибочную ситуацию: какой HTTP-статус вернётся, какой машиночитаемый код получит клиент, можно ли повторить запрос и какой идентификатор передать в поддержку. Это описание задаёт поведение API для известных случаев: неверных входных данных, недостаточных прав, конфликта версии или лимита. Баги сюда не относятся: их не планируют как часть контракта.

Версия контракта даёт потребителям время увидеть изменение и перейти на новую схему. В отчёте Postman за 2025 год функциональные и интеграционные тесты указали по 67% участников опроса, контрактные — 17%. Выборка не описывает весь рынок, но показывает, почему проверку совместимости нельзя оставлять на финальную неделю.

Проверка контракта на сценариях

Мы валидируем OpenAPI-файл и проверяем критичный обмен примерами: создание, успешный ответ, ошибка валидации, повтор с тем же ключом, устаревшее событие, конфликт версий, ограниченный доступ и проход по страницам.

В статье Schwarz и соавторов, опубликованной в 2025 году, такой подход связан с ранним обнаружением ломающих изменений между поставщиком и потребителем API. Он переводит продуктовые решения в проверяемые примеры поведения.

Минимальный артефакт перед разработкой помещается на одной странице: восемь строк из таблицы, два или три JSON-примера и имя владельца каждого решения. С ним команда оценивает правила, миграции, тесты и риски вместе с количеством эндпоинтов.

Решение, с которого можно начинать

API становится частью интеграции в тот момент, когда две системы одинаково понимают сущность, её изменения и границы доступа. Для первого шага мы выбираем один критичный сценарий и заполняем таблицу решений вместе с владельцем процесса, аналитиком и разработчиком.

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