Перейти к основному содержимому

Проектирование API и интеграций

Разработчику Архитектору Аналитику
Теория данных (раздел 3)

Проектирование API и интеграций

С чего начать

API — обещание потребителю; интеграция — его исполнение.

Сначала гарантии доставки и задержка (NFR), затем выбор синхронного HTTP или асинхронной шины.

Зрелость HTTP-интерфейса — модель Ричардсона.

Полный маршрут по главам — в конце статьи.

Современные программные системы редко существуют изолированно. Эффективность бизнес-процессов, масштабируемость приложений и гибкость ИТ-ландшафта зависят от того, насколько чётко и надёжно организованы взаимодействия между компонентами — как внутри системы, так и между различными системами. Эти взаимодействия реализуются через интеграции, а их интерфейсы — через интерфейсы прикладного программирования (API).

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

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

Интеграция с LLM-сервисами

Вызов 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 начинается с анализа:

  1. Определение потребителей — кто будет использовать API? Каковы их сценарии? Какие ограничения у них есть (языки, платформы, сетевые политики)?
  2. Формулировка контракта — какие операции должны быть доступны? Какие данные будут передаваться? Какие состояния системы изменяются?
  3. Согласование границ ответственности — где заканчивается зона ответственности API и начинается зона ответственности клиента? Это особенно важно при реализации сложной логики (например, обработки заказов, где часть валидации делегируется клиенту, часть — серверу).
  4. Оценка эволюционности — как 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;
  • политики версионирования и обратной совместимости;
  • механизм аутентификации и авторизации.

Интеграционный контракт — это основа для тестирования, сопровождения и аудита. Его отсутствие приводит к дрейфу интерфейсов, несогласованности в обработке ошибок и росту стоимости изменений.


Маппинг

Маппинг — это процесс трансформации данных из структуры, принятой в одной системе, в структуру, ожидаемую другой. Это осмысленное сопоставление семантики.


Уровни маппинга

  1. Синтаксический — соответствие формата — JSON ↔ XML, snake_case ↔ camelCase, кодировки (UTF-8 ↔ Windows-1251), типы данных (строка ↔ число с указанием маски). На этом уровне решаются технические несоответствия.

  2. Структурный — соответствие иерархии — плоская структура ↔ вложенная, массив ↔ объект с повторяющимися полями, отсутствие идентификатора в источнике ↔ требование GUID в приёмнике.

  3. Семантический — соответствие значений и смыслов:

    • status: "A"status: "active";
    • currency: "RUR"currency: "RUB" (с учётом устаревших кодов ISO 4217);
    • разные интерпретации даты (create_date в источнике — это момент создания документа, а в приёмнике — дата постановки в очередь);
    • нормализация единиц измерения (km/hm/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/&#123;id&#125; или 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 — для получения самих данных.


Пакетная и построчная передача

Оптимизация: адаптивные пакеты — размер порции регулируется динамически в зависимости от времени ответа и ошибок.


Парсинг данных

Парсинг — преобразование сериализованного потока (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);
  • статус обработки: uploadedvalidatedprocessed.

Обработка файла должна быть идемпотентной: повторная отправка того же файла (по хешу) не создаёт дубликат.


Безопасность, сеть и соединение при интеграциях

Безопасность интеграции — это многослойная защита.


Канальный уровень

  • 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) — реактивно, но требует надёжной доставки событий.
  • Гибрид — событие инициирует отложенную задачу, выполняемую в безопасное окно.

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


Интеграционные потоки

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

  1. Извлечение (Extract) — получение данных из источника (API, БД, файл).
  2. Трансформация (Transform) — маппинг, нормализация, валидация, обогащение (например, подтягивание справочника).
  3. Загрузка (Load) — запись в целевую систему.
  4. Подтверждение (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/&#123;id&#125;/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/&#123;orderId&#125;/line-itemsсостав заказа
Отгрузка/v1/orders/&#123;orderId&#125;/shipmentsфакт передачи на доставку
Событие отменыPOST /v1/orders/&#123;orderId&#125;/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-Key409 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/&#123;id&#125; (паттерн 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семантика (отгрузка до оплаты)исправить процесс
429rate limitbackoff по 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нет
200204 на том же методенет
Ужесточение валидации (раньше принимали пустой sku)часто нет для старых клиентов

Период deprecation — в ответе заголовок Sunset, в OpenAPI — deprecated: true, в документации — дата отключения v1.


Шаг 7 — проверка до production

  1. Контрактные тесты по OpenAPI (schemathesis, Dredd).
  2. Ручной прогон — Postman и curl, утилита curl, curl / fetch — примеры; для JVM — Практикум — API-тестер на Groovy.
  3. Нагрузка на GET /orders с курсором — Проверка надежности под нагрузкой.
  4. Чеклист уязвимостей API — 8.07.128.

Маршрут по материалам API

Связанные главы энциклопедии — один трек "от термина до production".


База и протокол

#ТемаСтатья
1Что такое API, REST, контракт2.09. API
2HTTP, заголовки, коды2.09. HTTP
3Веб-сервисы, SOAP2.09.115, 126 SOAP
4Ручная проверка2.09. Postman/curl, утилита curl, curl / fetch — примеры

Проектирование и архитектура

#ТемаСтатья
5Эта глава — интеграции, паттерны, маппинг7.06.117
5aПубличный API, OAuth 2.0, webhooks7.06.1171
5bmTLS, JWS, AsyncAPI, outbox7.06.1172
6Модель Ричардсона7.06.212
7Идемпотентность, Idempotency-Key7.06.213
8NFR интеграций7.06.1116
9OpenAPI, Swagger7.08.3
10Микросервисные паттерны (Saga, outbox)7.06.118, 2124 Saga

Инфраструктура и практика

#ТемаСтатья
11Проектирование API (углубление)8.05.122
12REST на практике8.05.1151
13Тестирование API7.05.2
14Безопасность API8.07.128
15Кэш и очереди рядом с API2.09.121 брокеры, 2.09.129 Redis

Разделы целиком: 2.09 — интеграция, 7.06 — проектирование, 8.05 — микросервисы.


Содержание