Проектирование API и интеграций
Модель и масштаб — Основы БД, опорные темы, проектирование БД, пакетная работа. Карта — о разделе.
Проектирование API и интеграций
API — обещание потребителю; интеграция — его исполнение.
Сначала гарантии доставки и задержка (NFR), затем выбор синхронного HTTP или асинхронной шины.
Зрелость HTTP-интерфейса — модель Ричардсона.
Полный маршрут по главам — в конце статьи.
Современные программные системы редко существуют изолированно. Эффективность бизнес-процессов, масштабируемость приложений и гибкость ИТ-ландшафта зависят от того, насколько чётко и надёжно организованы взаимодействия между компонентами — как внутри системы, так и между различными системами. Эти взаимодействия реализуются через интеграции, а их интерфейсы — через интерфейсы прикладного программирования (API).
Проектирование API и интеграций — это не просто техническая задача размещения методов и форматов обмена. Это комплексная дисциплина, охватывающая требования к функциональности, безопасность, отказоустойчивость, сопровождаемость и эволюционирование системы во времени. Ошибки, допущенные на этапе проектирования, проявляются позже — в виде нестабильных подключений, трудностей при обновлении, роста стоимости сопровождения или невозможности масштабировать интеграции.
Настоящая глава посвящена систематическому изложению принципов, подходов и методик, применяемых при проектировании API и интеграций. Акцент сделан на теоретических основах, но с привязкой к реальным инженерным практикам — без излишнего упрощения и без формул, чтобы сохранить пригодность текста для Markdown-публикации.
Вызов OpenAI, Anthropic или своего inference-endpoint — тот же класс задач — контракт (REST/streaming), аутентификация, квоты, идемпотентность длинных задач, логирование без утечки промптов. В семи слоях LLM-стека это слой 6; оркестрация RAG и tool calling — слой 4. Сопоставление с gateway и rate limit — в 12 концепциях.
API
API — это контракт между поставщиком функциональности и её потребителем. Он определяет, что можно сделать, как это сделать, в каком формате передаются данные и при каких условиях возможен вызов. API не является реализацией — это спецификация, которая может быть реализована множеством способов (локальная библиотека, сетевой сервис, встроенный модуль и т.п.).
Существует несколько уровней API:
- Внутренний (internal) — используется компонентами одной системы, часто не выставляется наружу. Пример: API ядра приложения, вызываемое его UI-слоем.
- Приватный (private) — доступен только ограниченному кругу доверенных систем (например, микросервисам внутри контура). Обычно защищён сетевыми ограничениями и строгой аутентификацией.
- Партнёрский (partner) — выставляется для интеграции с внешними организациями по договору. Управление доступом детализировано, мониторинг и логирование обязательны.
- Публичный (public) — открытый интерфейс, предназначенный для неограниченного круга разработчиков. Как правило, имеет документацию, SDK, песочницы и программы поддержки.
API не обязан быть сетевым. Хотя в современном контексте под API часто подразумевают веб-API (HTTP/REST, gRPC, GraphQL и др.), существуют и другие формы — библиотечные (C#-библиотека с public-методами), системные вызовы ОС, COM-интерфейсы, CLI-команды как API и т.д. Однако в рамках интеграций доминируют именно сетевые API — они обеспечивают слабую связанность, масштабируемость и независимость развёртывания.
Интеграции
Интеграция — это реализация взаимодействия двух или более систем с целью обмена данными, синхронизации состояния или делегирования функций. Она является исполнением контракта, заданного API.
В инженерной практике выделяют следующие типы интеграций:
- Точечная (point-to-point) — прямое соединение между двумя системами. Проста в реализации, но плохо масштабируется: при добавлении N систем количество соединений растёт как N·(N−1)/2. Применима при небольшом числе участников или в переходных сценариях.
- На основе шины сообщений (message bus) — системы взаимодействуют через промежуточный асинхронный брокер (Kafka, RabbitMQ, NATS и др.; теория очередей). Позволяет строить слабосвязанные, отказоустойчивые и масштабируемые архитектуры. Характерна для event-driven и микросервисных подходов.
- На основе ESB (Enterprise Service Bus) — централизованная инфраструктурная платформа, реализующая маршрутизацию, трансформацию, оркестрацию и управление интеграциями. Часто используется в legacy-средах; в новых проектах заменяется более лёгкими альтернативами (API Gateway + Event Broker).
- На основе API Gateway — шлюз централизует доступ ко множеству API, обеспечивая аутентификацию, ограничение скорости, кэширование, логирование и маршрутизацию. Особенно актуален при публикации микросервисов как единой точки входа.
Ключевой критерий выбора типа интеграции — бизнес-требования — допустимая задержка ответа, гарантии доставки, необходимость транзакционности, объём данных, частота обмена и уровень критичности интеграции.
Проектирование API
Проектирование API начинается с анализа:
- Определение потребителей — кто будет использовать API? Каковы их сценарии? Какие ограничения у них есть (языки, платформы, сетевые политики)?
- Формулировка контракта — какие операции должны быть доступны? Какие данные будут передаваться? Какие состояния системы изменяются?
- Согласование границ ответственности — где заканчивается зона ответственности API и начинается зона ответственности клиента? Это особенно важно при реализации сложной логики (например, обработки заказов, где часть валидации делегируется клиенту, часть — серверу).
- Оценка эволюционности — как API будет изменяться? Какие версии будут поддерживаться? Как избежать критических изменений (breaking changes)?
Проектирование API рекомендуется вести по принципу contract-first (сначала контракт, потом код) — сначала создаётся спецификация (OpenAPI/Swagger, protobuf, AsyncAPI и т.п.), затем — реализация и клиентские заглушки. Такой подход позволяет:
- проводить ревью контракта до написания кода;
- генерировать документацию и SDK автоматически;
- тестировать поведение клиента и сервера независимо;
- избежать дрейфа контракта при изменении реализации.
Паттерны проектирования API
Существует множество устоявшихся решений для типовых задач. Ниже — ключевые паттерны, применяемые при проектировании API:
Resource-Oriented Design (ROD)
Основан на REST-архитектуре, но без догматизма. API строится вокруг ресурсов — сущностей предметной области (пользователь, заказ, документ), каждая из которых имеет уникальный идентификатор (URI). Операции над ресурсами выражаются через стандартные HTTP-методы:
GET— чтение;POST— создание или запуск действия;PUT/PATCH— полное/частичное обновление;DELETE— удаление.
ROD обеспечивает предсказуемость, удобство кэширования и соответствие инфраструктурным инструментам (прокси, CDN, API Gateway).
Сводный чек-лист из восьми блоков (доменная модель в URL, методы, идемпотентность, коды ответа, версии, семантика путей, batch, query-язык) — в 2.09. API — восемь принципов RESTful API.
Action-Oriented Проектирование
Используется, когда операция не укладывается в CRUD-модель (например, "перевести средства", "отменить бронирование"). В REST-контексте такие операции оформляются как POST к подресурсу (/orders/{id}/cancel) или как RPC-стиль (POST /actions/cancelOrder). Важно: даже в RPC-подобных API стоит избегать глаголов в корневом пути (/createUser → /users с POST), чтобы сохранить семантическую ясность.
Pagination, Filtering, Sorting, Projection
Для управления объёмом данных в ответах применяются параметры:
- пагинация — в REST чаще всего одна схема на всё API —
page/size,offset/limit, непрозрачныйcursor, keyset (after_id), окно по времени (start_time/end_time) или гибрид; шесть схем с примерами и выбором; - фильтрация —
?status=active&created_after=2024-01-01; - сортировка —
?sort=created_at:desc,name:asc; - проекция —
?fields=id,name,email— клиент сам определяет, какие поля ему нужны.
Offset и page удобны для UI с номерами страниц, но при активных вставках и удалениях между запросами возможны дубликаты и пропуски; cursor и keyset устойчивее для лент и больших таблиц. Тот же keyset на стороне БД — WHERE id > :last вместо глубокого OFFSET — в SQL — пагинация.
Базовый разбор URL — в шпаргалке REST URL; практики именования, пагинации и кэша — дизайн REST API; единый язык query и batch — в восьми принципах RESTful API. Описание параметров в OpenAPI — техническое письмо, OpenAPI. Эти механизмы должны быть стандартизированы в рамках одного API.
HATEOAS (Hypermedia as the Engine of Application State)
Расширяет ответы ссылками на связанные ресурсы и доступные действия (например, в JSON — поле _links). Позволяет клиенту "обнаруживать" возможности API динамически, а не жёстко прописывать все URI. На практике используется редко из-за сложности клиентской обработки, но ценен для долгоживущих публичных API.
Error Handling и Соглашения об ошибках
На уровне процесса (исключение в коде, лог, error_id) опирайтесь на Ошибки, исключения и отказоустойчивость — в том числе ошибка vs исключение и отказоустойчивость сервисов. Здесь — контракт HTTP для потребителя API.
Единая структура ошибок критически важна. Рекомендуется:
- использовать HTTP-статусы семантично (4xx — ошибка клиента, 5xx — ошибка сервера);
- возвращать тело ошибки в формате, согласованном для всего API (например, RFC 7807 — Problem Details for HTTP APIs):
{
"type": "https://example.com/errors/validation",
"title": "Нарушение правил валидации",
"status": 400,
"detail": "Поле 'email' не соответствует формату",
"instance": "/orders/123",
"invalid-params": [
{ "name": "email", "reason": "must be a valid email address" }
]
}
- включать уникальный идентификатор ошибки (
error_id) для сопоставления с логами.
Проектирование интеграций
Проектирование интеграции начинается с анализа контекста взаимодействия. Необходимо чётко ответить на следующие вопросы:
- Какие системы участвуют и какова их роль? (источник данных, потребитель, оркестратор)
- Какова семантика обмена? (запрос-ответ, публикация событий, периодическая синхронизация, push-доставка)
- Каков объём и структура данных? (структурированные JSON/XML, бинарные файлы, потоковые данные)
- Какие гарантии требуются? (at-least-once, at-most-once, exactly-once; порядок доставки; транзакционность)
- Какова допустимая задержка? (реальное время, минуты, часы, пакетная обработка)
- Как обрабатываются сбои? (повторные попытки, dead-letter queues, уведомления оператору)
- Как обеспечивается наблюдаемость? (логирование, метрики, трассировка)
Если интеграционный payload хранится в PostgreSQL как JSONB, для проектирования фильтров, индексов и проверок полезен Практикум PostgreSQL по JSONB.
На основе этих ответов формулируется интеграционный контракт — документ, описывающий:
- форматы входных и выходных сообщений (включая схемы валидации);
- сценарии обработки ошибок и исключительных ситуаций;
- требования к производительности и SLA;
- политики версионирования и обратной совместимости;
- механизм аутентификации и авторизации.
Интеграционный контракт — это основа для тестирования, сопровождения и аудита. Его отсутствие приводит к дрейфу интерфейсов, несогласованности в обработке ошибок и росту стоимости изменений.
Маппинг
Маппинг — это процесс трансформации данных из структуры, принятой в одной системе, в структуру, ожидаемую другой. Это осмысленное сопоставление семантики.
Уровни маппинга
-
Синтаксический — соответствие формата — JSON ↔ XML, snake_case ↔ camelCase, кодировки (UTF-8 ↔ Windows-1251), типы данных (строка ↔ число с указанием маски). На этом уровне решаются технические несоответствия.
-
Структурный — соответствие иерархии — плоская структура ↔ вложенная, массив ↔ объект с повторяющимися полями, отсутствие идентификатора в источнике ↔ требование GUID в приёмнике.
-
Семантический — соответствие значений и смыслов:
status: "A"→status: "active";currency: "RUR"→currency: "RUB"(с учётом устаревших кодов ISO 4217);- разные интерпретации даты (
create_dateв источнике — это момент создания документа, а в приёмнике — дата постановки в очередь); - нормализация единиц измерения (
km/h↔m/s).
Семантический маппинг требует участия предметного эксперта — разработчик не может сам решить, что означает поле code в системе учёта ТМЦ, если оно не описано в контракте.
Подходы к реализации маппинга
- Статический (hardcoded) — логика трансформации зашита в код. Прост, но трудно поддерживать: любое изменение схемы требует пересборки и развёртывания.
- Конфигурационный (declarative) — правила задаются в виде конфигурации (JSON/YAML/XSLT/DSL). Позволяет изменять логику без перекомпиляции. Пример — JSONata, Jolt, XSLT, или кастомные DSL на основе ANTLR.
- Визуальный (graphical) — drag-and-drop интерфейсы (часто в ESB или iPaaS). Удобен для нетехнических пользователей, но страдает от ограниченной выразительности и сложности версионирования.
Оптимальный выбор зависит от частоты изменений, квалификации команды и требований к аудиту. Для критичных интеграций рекомендуется комбинировать декларативные правила с возможностью расширения через пользовательский код ("хуки").
Коннекторы
Коннектор — это компонент, отвечающий за физическое подключение к системе и абстрагирование деталей её API или протокола. Он инкапсулирует:
- параметры соединения (хост, порт, таймауты);
- механизм аутентификации (API-ключ, OAuth2-токен, TLS-сертификат);
- сериализацию/десериализацию;
- обработку сетевых ошибок и повторных попыток.
Коннектор может быть:
- Универсальным — параметризуется под разные эндпоинты (например, HTTP-коннектор, принимающий URL и заголовки);
- Специализированным — заточен под конкретную систему (SAP RFC-коннектор, 1С:Предприятие через COM или HTTP-сервис, Kafka-коннектор с поддержкой SASL/SSL).
Важно разделять логику интеграции (что делать с данными) и логику соединения (как доставить). Это позволяет:
- заменять реализацию коннектора без переписывания бизнес-логики;
- тестировать интеграцию с заглушками;
- стандартизировать обработку ошибок на уровне соединения.
Лучшая практика — реализовывать коннекторы как отдельные модули (библиотеки или микросервисы) со строгим интерфейсом — например, IConnector<TRequest, TResponse>, где SendAsync(TRequest) возвращает Task<TResponse> или ошибку.
Авторизация и аутентификация в интеграциях
Аутентификация ("кто вы?") и авторизация ("что вам разрешено?") — обязательные элементы любой интеграции, выходящей за пределы доверенной зоны.
Механизмы аутентификации
- API-ключ — простой, но уязвимый: ключ передаётся в заголовке (
X-API-Key) или параметре. Требует защиты канала (HTTPS), ротации и хранения в секрете. Подходит для внутренних или партнёрских интеграций с низким риском. - Basic Auth — передача
base64(username:password). Без HTTPS неприемлем. Рекомендуется использовать только с учётными записями с ограниченными правами. - OAuth 2.0 Client Credentials Flow — стандарт для сервер-серверных интеграций. Клиент получает временный токен доступа (
access_token) от Authorization Server, предъявляяclient_idиclient_secret. Преимущества — ограниченный срок жизни, централизованный аудит, отмена поclient_id. - mTLS (mutual TLS) — клиент и сервер проверяют друг друга по TLS-сертификатам. Обеспечивает сильную привязку к инфраструктуре; требует PKI. Широко используется в финансовых и государственных интеграциях.
- JWT (JSON Web Token) — самодостаточный токен, содержащий утверждения (claims). Позволяет делегировать авторизацию без обращения к центральному серверу на каждый запрос. Требует защиты закрытого ключа и механизма отзыва (через
jti+ блэклист или короткийexp).
Сводные потоки, таблица сравнения и выбор между JWT и API-ключом — авторизация в интеграционных сценариях.
Роль провайдеров в контексте авторизации
Провайдер авторизации — это система, выдающая и проверяющую учётные данные или токены. В интеграциях выделяют:
- Identity Provider (IdP) — хранит учётные записи (Keycloak, Auth0, Azure AD, Okta). Отвечает за аутентификацию.
- Authorization Server (AS) — выдаёт токены с правами (scopes, roles). Может быть тем же сервисом, что и IdP, или отдельным (например, в архитектуре UMA — User-Managed Access).
- Resource Server (RS) — защищает API и проверяет токены.
В интеграциях между системами обычно используется Machine-to-Machine (M2M) сценарий, где IdP/AS выдаёт токен сервисному аккаунту, а не человеку. Важно, чтобы права этого аккаунта были минимальны (принцип наименьших привилегий) и привязаны к конкретной интеграции.
Паттерны для интеграций
Помимо типов (точка-точка, шина и т.д.), существуют устоявшиеся поведенческие паттерны, решающие конкретные задачи.
Request-Reply и Fire-and-Forget
- Request-Reply — синхронный вызов с ожиданием ответа. Прост, но создаёт жёсткую связанность по времени. Подходит для операций с немедленным результатом (проверка баланса, получение справочника).
- Fire-and-Forget — отправка сообщения без ожидания. Асинхронно, устойчиво к сбоям получателя. Требует механизмов подтверждения (например, отдельный эндпоинт
/status/{id}или callback).
Polling и Webhooks и Streaming
- Polling — клиент периодически опрашивает сервер (
GET /updates?since=…). Надёжен, но неэффективен при низкой частоте изменений. - Webhooks — сервер уведомляет клиента по HTTP-POST при событии. Эффективен, но требует от клиента публичного эндпоинта и защиты от подделки (подписи, shared secret).
- Streaming — постоянное соединение (WebSocket, SSE, gRPC streaming). Подходит для high-throughput или low-latency сценариев (трейдинг, IoT).
Saga Pattern
Для распределённых транзакций, когда ACID невозможен. Последовательность локальных транзакций с компенсирующими действиями ("откатами") в случае сбоя. Управление может быть:
- Оркестровано (orchestrated) — центральный координатор (например, интеграционный движок);
- Хореографировано (choreographed) — каждая система реагирует на события самостоятельно.
Настройка интеграций
Настройка — это задание параметров, которые могут изменяться без перекомпиляции — URL, таймауты, учётные данные, правила маппинга, политики повторов.
Ключевые принципы:
- Внешние параметры (endpoints, credentials) хранятся вне кода — в переменных окружения, конфиг-файлах, vault-системах (HashiCorp Vault, AWS Secrets Manager).
- Изменяемая логика (правила фильтрации, маппинга) выносится в конфигурацию, версионируемую вместе с кодом.
- Настройки производительности (пулы соединений, лимиты скорости) выносятся в отдельный профиль (dev/test/prod).
Важно обеспечить валидацию конфигурации при запуске. Интеграция, которая "падает" через час после старта из-за опечатки в URL, — признак плохого проектирования.
Методы API
Метод API — это операция, доступная потребителю через интерфейс. В веб-API методы обычно выражаются через HTTP-глаголы, но их смысл выходит за рамки протокола.
CRUD и за его пределами
Базовые операции — Create, Read, Update, Delete — соответствуют POST, GET, PUT/PATCH, DELETE. Однако реальные системы часто требуют операций, не укладывающихся в эту модель:
- Partial Update (
PATCH) — изменение отдельных полей сущности. Требует чёткого контракта — какие поля можно менять одновременно, есть ли условия применимости (например, статус должен быть "draft"). - Upsert — создание или обновление по идентификатору. Реализуется через PUT с идентификатором в URI, где отсутствие ресурса трактуется как создание. Альтернатива — POST с идемпотентным ключом в заголовке
Idempotency-Key.
PUT /users/123
Content-Type: application/json
{ "name": "Alice", "email": "alice@example.com" }
POST /users
Idempotency-Key: 7f3c9a2e-4b1d-4e8a-9c0f-1a2b3c4d5e6f
Content-Type: application/json
{ "name": "Bob" }
- Bulk Operations — массовая обработка —
POST /users/bulk-create,POST /orders/bulk-update, смешанный CRUD в одном запросе. Экономит round-trip time, но увеличивает сложность обработки ошибок (частичный успех?). Рекомендуется возвращать детализированный отчёт по каждой сущности. Теория — Пакетная работа с данными. - Search / Query — сложные выборки, выходящие за рамки фильтрации по полям (геопространственные запросы, полнотекстовый поиск, агрегации). Часто оформляются как отдельный эндпоинт (
POST /search) с телом запроса в DSL (например, Elasticsearch Query DSL), чтобы не перегружать URI.
Важно: метод должен быть идемпотентным, если это возможно. Идемпотентность (GET, PUT, DELETE) означает, что повторный вызов не изменяет состояние системы. Это критично для устойчивости к сетевым сбоям и retry-логике.
Long-Running Operations
Операции, превышающие допустимое время ответа (например, формирование отчёта), реализуются через asynchronous request-reply:
POST /reports
Content-Type: application/json
{ "from": "2026-01-01", "to": "2026-01-31" }
→ 202 Accepted
Location: /reports/42
GET /reports/42
→ 202 Accepted (ещё в процессе)
→ 200 OK (готово, тело с результатом)
Статус операции может включать — queued, processing, completed, failed, с дополнительными полями (progress_percent, error_details). Такой подход предотвращает таймауты и позволяет масштабировать обработку.
Объекты и сущности в интеграции
Сущность — это предметная модель, передаваемая через интеграцию. Это представление бизнес-концепции — клиент, заказ, счёт, событие.
Идентификация и ссылочная целостность
Каждая сущность должна иметь устойчивый идентификатор, не зависящий от реализации одной из систем:
- не
id(внутренний первичный ключ СУБД); - а
external_id,global_id,urn:Система:entity:type:id.
Ссылки между сущностями передаются через эти идентификаторы. Если система не поддерживает глобальные ID, их генерирует интеграционный слой (например, mapping-таблица "локальный ID ↔ глобальный ID").
Версионирование сущностей
Структура сущности может меняться. Чтобы избежать критических изменений:
- добавляются новые поля без удаления старых;
- устаревшие поля помечаются (в документации —
deprecated, в OpenAPI —x-deprecated: true); - вводится версионирование схемы (
schema_version: "2.1"в теле или заголовкеX-Schema-Version).
В идеале, сущность содержит метаданные — created_at, updated_at, source_system, event_id — это упрощает диагностику и аудит.
Логирование в интеграциях
Логирование — механизм обеспечения наблюдаемости, аудита и восстановления.
Уровни логирования
- Трассировка (trace) — полный dump запроса/ответа (только в тестовой среде или по флагу). Содержит тело, заголовки, время.
- Отладка (debug) — этапы обработки — "начал маппинг", "отправка в X", "получен ответ 200".
- Информация (info) — ключевые события — "интеграция Z запущена", "обработано 125 записей", "файл Y загружен".
- Предупреждение (warn) — ситуативные отклонения — "поле X отсутствует, использовано значение по умолчанию", "повторная попытка №2".
- Ошибка (error) — сбой, требующий вмешательства — "неверный токен", "таймаут соединения", "нарушение схемы".
Требования к логам
- Каждое сообщение должно содержать correlation ID — уникальный идентификатор цепочки вызовов, передаваемый через заголовок (
X-Correlation-ID). Позволяет собрать полную картину по одной транзакции. - Избегать логирования секретов (токены, пароли, персональные данные) — маскировать или исключать.
- Логи должны быть структурированы (JSON) для удобства индексации в ELK, Grafana Loki и т.п.
Логирование — это часть SLA. Если интеграция не логирует критические операции, она считается непригодной для production.
Получение и передача данных
Эти процессы определяют направление и инициатора обмена.
Pull и Push
- Pull (опрос) — потребитель запрашивает данные у поставщика. Преимущества: контроль нагрузки, простота авторизации (токен на стороне клиента). Недостатки: задержка, избыточные запросы.
- Push (толкание) — поставщик инициирует отправку при изменении. Преимущества: минимальная задержка. Недостатки: необходимость публичного эндпоинта у получателя, управление доставкой.
Выбор зависит от требований к latency и control. Часто используется гибрид: push для уведомлений ("данные изменились"), pull — для получения самих данных.
Пакетная и построчная передача
- Пакетная (batch) — данные передаются порциями (100, 1000 записей). Эффективна по сети и ресурсам СУБД. Требует механизмов продолжения (cursor, watermark). Теория batch/bulk/chunk, транзакций на чанк и checkpoint — Пакетная работа с данными; риски batch-окна и сверки с продом — пакетная загрузка в интеграционных потоках; контракт HTTP batch — принцип 7 в REST API. Альтернатива "один batch-job" — нарезка на сообщения и воркеры.
- Построчная (record-by-record) — каждая запись — отдельный запрос. Просто, но дорого. Допустима только при очень малых объёмах.
Оптимизация: адаптивные пакеты — размер порции регулируется динамически в зависимости от времени ответа и ошибок.
Парсинг данных
Парсинг — преобразование сериализованного потока (JSON, XML, CSV, EDI) в структуру, понятную приложению.
Подходы
- Валидирующий парсинг — парсер одновременно проверяет соответствие схеме (JSON Schema, XSD). Ошибка парсинга = ошибка валидации. Надёжно, но требует поддержки схем.
- Лояльный парсинг (lenient parsing) — игнорирование неизвестных полей, приведение типов ("123" → 123). Удобен для обратной совместимости, но рискован: ошибка может проявиться позже.
- Потоковый парсинг — обработка без полной загрузки в память (SAX для XML,
JsonReaderв .NET/Java). Необходим для больших файлов.
Парсер должен сообщать не только что не распарсилось, но и где — путь в структуре ($.items[5].price), ожидаемый тип, полученное значение. Это ускоряет диагностику.
Таблица "на что проверять и с чем сравнивать" на уровне функции — Проверка и валидация.
Работа с файлами и бинарными данными
Передача файлов — частный случай интеграции, требующий особых решений.
Методы передачи
- В теле запроса (multipart/form-data) — стандарт для веб-форм. Поддерживается большинством фреймворков. Ограничения: размер, необходимость буферизации на сервере.
- Прямая загрузка в хранилище — клиент получает временный URL (pre-signed URL в S3, SAS-токен в Azure Blob) и загружает напрямую. Снимает нагрузку с API-сервера.
- Потоковая передача (chunked transfer) — файл разбивается на части, каждая — отдельный запрос с указанием номера и контрольной суммы. Позволяет возобновлять загрузку.
Метаданные и согласованность
Файл сам по себе — недостаточно. Необходимы:
- имя, MIME-тип, размер;
- контрольная сумма (MD5, SHA-256) для проверки целостности;
- связь с бизнес-сущностью (
document_id,order_number); - статус обработки:
uploaded→validated→processed.
Обработка файла должна быть идемпотентной: повторная отправка того же файла (по хешу) не создаёт дубликат.
Безопасность, сеть и соединение при интеграциях
Безопасность интеграции — это многослойная защита.
Канальный уровень
- HTTPS (TLS 1.2+) — обязательно. Отключать проверку сертификата (
insecure=true) недопустимо даже в тестовой среде. - MTLS — для высокозащищённых интеграций: клиент и сервер аутентифицируют друг друга по сертификатам.
- IP-фильтрация — whitelist на уровне firewall или API Gateway.
Уровень приложения
- Rate limiting — ограничение запросов в секунду/минуту по ключу (API key, IP, client_id). Защита от DoS и ошибок клиента. Token bucket для burst, leaky bucket для ровного потока — пять алгоритмов.
- Request validation — проверка размера тела, глубины вложенности JSON, длины строк.
- Санитизация — при передаче данных в SQL, командную строку или HTML — обязательное экранирование.
Сетевые аспекты
- Таймауты должны быть дифференцированы —
connect,read,write,total. Например: 5 с на подключение, 30 с на чтение. - Keep-Alive — повторное использование TCP-соединений. Уменьшает накладные расходы.
- Ретраи с экспоненциальной задержкой — при временных сбоях (5xx, таймаут). Максимум 3–5 попыток, с jitter для избежания волнового эффекта.
Сеть ненадёжна. Интеграция должна проектироваться как частично доступная система, согласно теореме CAP.
Первичная загрузка данных
Первичная загрузка — это инициализация интеграции: перенос исторических данных из источника в целевую систему. Это уникальный по сложности этап, поскольку:
- объёмы данных обычно максимальны;
- нет предыдущего состояния для сравнения;
- система-приёмник может быть не готова к нагрузке;
- ошибки критичны: некорректная первоначальная загрузка порождает длительные расхождения.
Подходы
-
Полная выгрузка и загрузка — источник выдаёт snapshot (например, дамп БД или файл со всеми записями на дату T), приёмник его обрабатывает. Просто, но требует остановки изменений в источнике или read-only режима. Применимо при небольших объёмах или off-peak окнах.
-
Инкрементальная инициализация — данные переносятся порциями, параллельно с работой систем. Источник продолжает принимать изменения, которые либо игнорируются (и загружаются позже), либо записываются в отдельный буфер (например, change log). Сложнее, но минимизирует downtime.
-
Гибридный подход — snapshot + replay изменений, произошедших во время загрузки. Требует:
- фиксации временной метки T_start;
- выгрузки данных по состоянию на T_start;
- получения всех событий из T_start до T_end (лог репликации, CDC, журнал аудита);
- применения изменений поверх snapshot.
Практические рекомендации
- Разделять загрузку на логические этапы: справочники → мастер-данные → транзакционные данные.
- Проводить валидацию контрольных сумм и объёмов на каждом этапе (например, количество записей в источнике и приёмнике должно совпадать).
- Предусматривать механизм отката — если загрузка прервана, система должна вернуться в исходное состояние, а не остаться в частично загруженном.
- Фиксировать в логах метки времени T_start, T_end, версии схем, идентификаторы дампов — это критично для аудита.
Первичная загрузка — проектная задача. Её успех зависит от координации команд, чёткого плана и контроля метрик. Инженерные приёмы (чанки, staging, идемпотентность, перезапуск с checkpoint) — Пакетная работа с данными.
Обновление и периодическая загрузка
После первичной загрузки начинается поддержание актуальности — синхронизация состояния.
Механизмы синхронизации
-
Полная повторная загрузка — используется редко — только для небольших справочников, где проще перезалить всё, чем вычислять дельту. Плюс: простота, гарантия целостности. Минус: нагрузка, задержка актуальности.
-
Дельта-синхронизация по временной метке — запрос данных, изменённых после last_sync_time. Требует:
- наличия поля
updated_atс высокой точностью (миллисекунды, монотонность); - учёта временного рассогласования (например, клиент в UTC+3, сервер в UTC — смещение при сравнении);
- обработки "движущихся окон" (запись изменяется во время выборки дельты).
- наличия поля
-
Дельта по идентификатору —
WHERE id > last_processed_id. Работает только при монотонно растущих ID (например,BIGINT IDENTITY). Не учитывает обновления. -
Change Data Capture (CDC) — перехват изменений на уровне СУБД (лог транзакций, триггеры, расширения вроде Debezium). Обеспечает минимальную задержку и полноту, но требует доступа к инфраструктуре источника.
-
Событийная синхронизация — при каждом изменении публикуется событие ("UserUpdated", "OrderCancelled"), потребитель его обрабатывает. Максимально эффективно, но требует event-driven архитектуры.
Частота и триггеры
- По расписанию (cron) — просто, предсказуемо. Риск: обработка в пик нагрузки.
- По событию (event-triggered) — реактивно, но требует надёжной доставки событий.
- Гибрид — событие инициирует отложенную задачу, выполняемую в безопасное окно.
Важно: каждая синхронизация должна быть идемпотентной. Повторная обработка одной и той же дельты не должна нарушать целостность.
Интеграционные потоки
Интеграционный поток — это последовательность операций, преобразующих данные от источника до финального потребителя. Поток может включать:
- Извлечение (Extract) — получение данных из источника (API, БД, файл).
- Трансформация (Transform) — маппинг, нормализация, валидация, обогащение (например, подтягивание справочника).
- Загрузка (Load) — запись в целевую систему.
- Подтверждение (Acknowledge) — уведомление источника об успешной обработке (для упорядоченных систем).
Потоки могут быть:
- Линейными — A → B → C. Просты, но не гибки.
- Разветвлёнными — A → (B, C), где B и C работают параллельно.
- Циклическими — результат C влияет на A (осторожно: риск дивергенции).
Управление состоянием потока
Состояние потока должно быть устойчивым к сбоям. Это достигается через:
- Журналирование прогресса — фиксация "последней обработанной записи" в отдельном хранилище (например, таблица
integration_progress). - Транзакционная обёртка — операции "загрузка + запись прогресса" выполняются в одной транзакции (границы чанка и атомарность — Пакетная работа с данными).
- Компенсация — при ошибке на этапе C откатить B и A.
Инструменты оркестрации (Apache Airflow, Prefect, Temporal) позволяют декларативно описывать потокы и автоматически управлять состоянием, retry-логикой и зависимостями. См. также ETL-ELT и оркестрацию.
Права доступа в интеграциях
Права доступа — это детализированная политика, основанная на принципе наименьших привилегий.
Гранулярность
- На уровне эндпоинта —
/usersдоступен,/users/{id}/reset-password— нет. - На уровне HTTP-метода —
GET /ordersразрешён,DELETE /orders— запрещён. - На уровне данных — клиент видит только записи с
tenant_id = X(multi-tenancy). - На уровне поля — в ответе
GET /usersполеsalaryскрыто для ролиexternal_partner.
Механизмы реализации:
- RBAC (Role-Based Access Control) — права привязаны к ролям, роли — к сервисным аккаунтам. Просто, но может привести к "вздутию ролей".
- ABAC (Attribute-Based Access Control) — политики выражаются через атрибуты субъекта, ресурса, действия и контекста (например,
user.department == resource.owner_department && time.hour < 18). Гибко, но сложнее в аудите. - ReBAC (Relationship-Based Access Control) — права строятся на отношениях ("user → member_of → team → owns → project"). Подходит для сложных организационных структур (Authzed, SpiceDB).
В интеграциях предпочтителен ABAC с чёткими атрибутами в токене (например, scope: orders:read, tenant_id: "789"). Это позволяет централизовать политики и избежать дублирования логики в каждом API.
Аудит прав
Каждое предоставление доступа должно логироваться — кто, кому, какие права, на каком основании, на какой срок. Это требование регуляторов (GDPR, 152-ФЗ) и внутренних политик безопасности.
Сквозной пример — партнёрский API заказов
Второй сценарий (публичный API, OAuth 2.0, входящие webhooks) — Публичный API, OAuth 2.0 и webhooks.
Третий сценарий (mTLS, JWS-webhooks, AsyncAPI, transactional outbox) — mTLS, JWS-подпись webhooks и AsyncAPI с outbox.
Ниже — как спроектировать RESTful HTTP API, которое годами обслуживает внешних потребителей (склад, маркетплейс, ERP партнёра). Домен упрощён, но шаги те же, что на реальном проекте.
Контекст и потребители
Система: интернет-магазин отдаёт партнёрскому складу заказы на отгрузку.
| Потребитель | Сценарии | Ограничения |
|---|---|---|
| WMS партнёра | забрать новые заказы, подтвердить отгрузку, сообщить об отмене | batch до 100 заказов, retry при таймаутах |
| Внутренний мониторинг | статус интеграции, dead-letter | только чтение |
| Аналитика (batch) | выгрузка за сутки | отдельный rate limit |
NFR (выдержка): p95 ответа GET списка — до 800 мс; гарантия at-least-once доставки событий от партнёра; SLA доступности API — 99,5%. Подробнее про формулировку NFR — Проектирование под нефункциональные требования.
Шаг 1 — модель ресурсов (не таблиц БД)
Ресурсы отражают язык партнёра, а не внутреннюю схему PostgreSQL:
| Ресурс | URI (коллекция) | Смысл |
|---|---|---|
| Заказ | /v1/orders | заказ магазина, видимый складу |
| Позиция заказа | /v1/orders/{orderId}/line-items | состав заказа |
| Отгрузка | /v1/orders/{orderId}/shipments | факт передачи на доставку |
| Событие отмены | POST /v1/orders/{orderId}/cancellation | действие вне чистого CRUD |
Имена — существительные во множественном числе, версия в префиксе пути (/v1/), чтобы не ломать старых клиентов при эволюции. Уровень зрелости такого API — 2 по Ричардсону (Уровни развития API и модель Ричардсона); HATEOAS для B2B-интеграций чаще ограничивают ссылкой next в пагинации.
Шаг 2 — contract-first (фрагмент OpenAPI 3)
Сначала согласуют YAML, потом пишут обработчики и генерируют mock для склада:
Код ITЗагрузка примера кода…
Документирование и ревью контракта — Swagger/OpenAPI. Ввод по терминам API — 2.09. API.
Шаг 3 — примеры HTTP-обмена
Список заказов (курсорная пагинация) — сравнение с offset, page и keyset в шести схемах пагинации:
GET /v1/orders?status=ready_to_ship&limit=50 HTTP/1.1
Host: api.shop.example
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Accept: application/json
X-Correlation-ID: 9f2a1c0b-7e3d-4a1f-b2c8-001122334455
Код ITЗагрузка примера кода…
Создание с идемпотентностью (повтор после таймаута не создаёт второй заказ — см. Методы и ключ идемпотентности):
POST /v1/orders HTTP/1.1
Host: api.shop.example
Authorization: Bearer …
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
{
"externalRef": "WMS-2026-00482",
"lineItems": [{ "sku": "SKU-99", "quantity": 1 }]
}
HTTP/1.1 201 Created
Location: /v1/orders/8c4d2e9f-7b3e-4f12-9f01-dd22ee334455
Content-Type: application/json
{
"id": "8c4d2e9f-7b3e-4f12-9f01-dd22ee334455",
"externalRef": "WMS-2026-00482",
"status": "created"
}
Повтор того же Idempotency-Key → 409 Conflict или 200 OK с тем же телом, что при первом успешном вызове (политика фиксируется в контракте).
Действие "отменить" — подресурс, не глагол в корне:
POST /v1/orders/7b3e9f12-8c4a-4d2e-9f01-aa11bb22cc33/cancellation HTTP/1.1
Content-Type: application/json
{ "reason": "out_of_stock", "comment": "SKU-42 нет на складе" }
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"orderId": "7b3e9f12-8c4a-4d2e-9f01-aa11bb22cc33",
"status": "cancellation_pending"
}
Долгая обработка — 202 Accepted + опрос GET /v1/orders/{id} (паттерн long-running operation выше в этой главе).
Шаг 4 — единый формат ошибок
Внешний потребитель должен программно отличать "исправь запрос" от "повтори позже":
{
"type": "https://api.shop.example/problems/validation",
"title": "Ошибка валидации",
"status": 400,
"detail": "Поле lineItems[0].quantity должно быть >= 1",
"instance": "/v1/orders",
"traceId": "9f2a1c0b-7e3d-4a1f-b2c8-001122334455",
"errors": [
{ "field": "lineItems[0].quantity", "code": "min_value" }
]
}
| HTTP | Когда | Действие клиента |
|---|---|---|
| 400 | неверный JSON, схема | исправить запрос |
| 401 / 403 | токен, scope | обновить авторизацию |
| 404 | нет ресурса | прекратить retry |
| 409 | конфликт статуса, дубликат | бизнес-обработка |
| 422 | семантика (отгрузка до оплаты) | исправить процесс |
| 429 | rate limit | backoff по Retry-After |
| 5xx | сбой сервера | retry с jitter |
Справочник всех классов и редких кодов (418, 422, 451, WebDAV) — HTTP, шпаргалка по статусам.
Шаг 5 — DTO и внутренняя модель
В БД заказ хранится с tenant_id, payment_status, внутренними флагами. В API партнёру отдают урезанный DTO:
{
"id": "uuid",
"externalRef": "string",
"status": "ready_to_ship",
"lineItems": [{ "sku": "string", "quantity": 1 }]
}
Поля paymentIntentId, marginPercent в ответ не попадают — это маппинг presentation layer (практика в 8.05).
Шаг 6 — что ломает контракт
| Изменение | Обратная совместимость |
|---|---|
| Новое необязательное поле в ответе | да |
| Новый query-параметр с default | да |
| Удаление поля из ответа | нет → новая версия /v2/ |
Смена типа quantity string → number | нет |
200 → 204 на том же методе | нет |
Ужесточение валидации (раньше принимали пустой sku) | часто нет для старых клиентов |
Период deprecation — в ответе заголовок Sunset, в OpenAPI — deprecated: true, в документации — дата отключения v1.
Шаг 7 — проверка до production
- Контрактные тесты по OpenAPI (schemathesis, Dredd).
- Ручной прогон — Postman и curl, утилита curl, curl / fetch — примеры; для JVM — Практикум — API-тестер на Groovy.
- Нагрузка на
GET /ordersс курсором — Проверка надежности под нагрузкой. - Чеклист уязвимостей API — 8.07.128.
Маршрут по материалам API
Связанные главы энциклопедии — один трек "от термина до production".
База и протокол
| # | Тема | Статья |
|---|---|---|
| 1 | Что такое API, REST, контракт | 2.09. API |
| 2 | HTTP, заголовки, коды | 2.09. HTTP |
| 3 | Веб-сервисы, SOAP | 2.09.115, 126 SOAP |
| 4 | Ручная проверка | 2.09. Postman/curl, утилита curl, curl / fetch — примеры |
Проектирование и архитектура
| # | Тема | Статья |
|---|---|---|
| 5 | Эта глава — интеграции, паттерны, маппинг | 7.06.117 |
| 5a | Публичный API, OAuth 2.0, webhooks | 7.06.1171 |
| 5b | mTLS, JWS, AsyncAPI, outbox | 7.06.1172 |
| 6 | Модель Ричардсона | 7.06.212 |
| 7 | Идемпотентность, Idempotency-Key | 7.06.213 |
| 8 | NFR интеграций | 7.06.1116 |
| 9 | OpenAPI, Swagger | 7.08.3 |
| 10 | Микросервисные паттерны (Saga, outbox) | 7.06.118, 2124 Saga |
Инфраструктура и практика
| # | Тема | Статья |
|---|---|---|
| 11 | Проектирование API (углубление) | 8.05.122 |
| 12 | REST на практике | 8.05.1151 |
| 13 | Тестирование API | 7.05.2 |
| 14 | Безопасность API | 8.07.128 |
| 15 | Кэш и очереди рядом с API | 2.09.121 брокеры, 2.09.129 Redis |
Разделы целиком: 2.09 — интеграция, 7.06 — проектирование, 8.05 — микросервисы.