Как мы строим интеграции: архитектура, стек, подход
Нужна автоматизация? Обсудим вашу задачу бесплатно
Написать в TelegramКогда бизнес просит «связать Ozon с МойСклад» — это звучит просто. На деле за каждой интеграцией стоит архитектура, которая определяет, будет ли система работать надёжно через год или сломается при первой нагрузке.
В этой статье разберём, как мы проектируем интеграции — от выбора стека до мониторинга в продакшене. Не абстрактные best practices, а конкретные решения, которые мы применяем в каждом проекте — и почему именно такие.
Почему «скрипт на cron» — это не интеграция
Самый частый запрос, с которого начинается разговор: «У нас есть скрипт, который раз в час забирает данные из API и кладёт в таблицу. Он иногда падает. Можете починить?»
Мы открываем код и видим одно и то же. Функция, которая делает fetch к API, парсит ответ, записывает в базу. Запускается по cron. Без обработки ошибок, без retry, без логов. Если API вернул 500 — скрипт упал, данные за этот час потеряны. Если вернул 429 (rate limit) — то же самое. Если ответил медленно и наступил таймаут — скрипт повис до следующего запуска, а следующий запуск стартовал параллельно и создал дубликаты.
Вот конкретные проблемы, которые мы встречаем в таких «интеграциях» раз за разом:
Потеря данных при сбоях. API на той стороне вернул ошибку — заказ, обновление цены, документ не обработался. Следующий запуск берёт только новые данные. Пропущенные — потеряны навсегда. Никто не узнает, пока клиент не позвонит и не спросит «где мой заказ?».
Дубликаты при повторах. Запрос ушёл, ответ не пришёл — таймаут. Заказ создался или нет? Скрипт не знает. При повторном запуске создаёт ещё раз. У клиента два одинаковых заказа в МойСклад.
Отсутствие retry. Большинство ошибок внешних API — временные. Сервер перегружен, сеть моргнула, rate limit. Через 10 секунд тот же запрос пройдёт. Но скрипт на cron не умеет ждать и повторять — он просто падает и ждёт следующего запуска через час.
Нет мониторинга. Скрипт упал в три часа ночи. Менеджер узнал утром, когда заказы перестали появляться в системе. Или не узнал вообще, потому что заказов в тот день было немного и расхождение никто не заметил.
Это не интеграция. Это автоматизация хорошей погоды: работает, пока всё идеально. А в работе с внешними API «идеально» не бывает никогда.
Наша архитектура: три слоя
Каждую интеграцию мы проектируем как конвейер из трёх слоёв. Источник, обработка, доставка. Между ними — очереди. Это не изобретение, это стандартная ETL-архитектура, адаптированная под реалии бизнес-интеграций: нестабильные API, rate limits, непредсказуемые объёмы данных.
1. Слой получения данных (Source)
Первый вопрос при проектировании: как забирать данные из источника?
API polling — самый частый вариант. Сервис каждые N секунд опрашивает API на предмет новых событий. Flowwow не отправляет вебхуки — мы polling’им их API каждые 30 секунд. Ozon Seller API тоже лучше работает в pull-модели: вебхуки ненадёжны, документация неполная. Polling предсказуем и не зависит от инфраструктуры на стороне API.
Webhooks — когда API умеет отправлять уведомления. МойСклад, Telegram Bot API, Stripe. Быстрее polling’а — данные приходят в момент события. Но требуют публичный endpoint, верификацию подписи, обработку дубликатов (webhook может прийти дважды). Мы используем вебхуки, когда API это поддерживает и документация внятная.
Файловый обмен — для систем, у которых нет API. Выгрузка в CSV, XML, Excel. Файл появляется на FTP или в Google Drive — сервис подхватывает, парсит, обрабатывает.
Для каждого источника мы решаем три вопроса:
- Rate limits. Ozon API даёт 60 запросов в минуту. Google Sheets API — 300 в минуту на проект. Мы закладываем межзапросные паузы и никогда не работаем на пределе лимита. Запас — минимум 30%.
- Пагинация. API отдаёт данные страницами: 100 товаров за запрос. Если товаров 4 000 — это 40 последовательных запросов. Скрипт обязан обрабатывать пагинацию корректно, включая случай, когда данные изменились между страницами.
- Аутентификация. OAuth, API-ключи, токены с истекающим сроком. Refresh-логика автоматическая: если токен протух — обновляем и повторяем запрос, а не падаем с ошибкой 401.
2. Слой обработки (Processing)
Сердце интеграции. Здесь данные из формата источника превращаются в формат получателя, проходят валидацию и бизнес-логику.
Очередь задач — обязательна. Каждая единица работы (заказ, обновление цены, документ) попадает в очередь. Не обрабатывается сразу, а ставится в очередь и обрабатывается worker’ом. Это даёт три вещи: последовательность (не бывает race condition), персистентность (при перезапуске задачи не теряются), наблюдаемость (видно, сколько задач ждёт, сколько обработано, сколько упало).
На Node.js мы используем BullMQ + Redis. Типичная конфигурация:
const queue = new Queue('orders', {
connection: { host: 'localhost', port: 6379 },
defaultJobOptions: {
attempts: 5,
backoff: { type: 'exponential', delay: 3000 },
removeOnComplete: { age: 7 * 24 * 3600 },
removeOnFail: false, // упавшие задачи остаются для разбора
},
});
removeOnFail: false — принципиальный момент. Упавшие задачи не удаляются. Они остаются в dead letter queue, пока кто-то не разберётся, что произошло. Автоматизация, которая молча теряет данные, хуже ручной работы.
Retry с exponential backoff. Первая попытка — через 3 секунды. Вторая — через 9. Третья — через 27. Пятая — через 4 минуты. Если API перегружен, забрасывать его запросами каждые 3 секунды — значит усугублять проблему. Нарастающая пауза даёт сервису время восстановиться.
Бизнес-логика. Трансформация данных между форматами двух систем. Маппинг полей, конвертация валют, расчёт цен с учётом комиссий, валидация обязательных полей. Если данные невалидны — задача не повторяется (бесполезно), а сразу уходит в dead letter queue с описанием проблемы.
3. Слой доставки (Destination)
Запись обработанных данных в целевую систему. Звучит просто — но здесь скрывается самый коварный класс ошибок.
Идемпотентность — обязательна. Если retry автоматический, каждая операция записи обязана быть идемпотентной. Это значит: повторное выполнение с теми же данными даёт тот же результат без побочных эффектов. Пример: сервис создаёт заказ в МойСклад, запрос ушёл, ответ не пришёл — таймаут. Заказ создался или нет? Неизвестно. Retry создаёт заказ ещё раз. Без идемпотентности — у клиента два одинаковых заказа.
Решение: каждый объект создаётся с уникальным внешним ID — например, ID заказа из Flowwow. Перед записью сервис проверяет: существует ли объект с таким внешним ID? Если да — пропускаем или обновляем. Если нет — создаём. Повторный вызов безопасен.
Обработка конфликтов. Целевая система может отклонить запись: товар удалён, контрагент заблокирован, поле стало обязательным после обновления API. Каждый случай обрабатывается: retryable-ошибки повторяются, non-retryable — уходят в dead letter queue с понятным описанием для человека.
Стек
| Компонент | Технология | Почему |
|---|---|---|
| Runtime | Node.js + TypeScript | Типизация ловит ошибки до продакшена. Экосистема пакетов. Async/await идеален для I/O-bound интеграций |
| Очереди | BullMQ (Redis) | Retry, приоритеты, rate limiting, dead letter queue из коробки. Redis — персистентность |
| Хранение | PostgreSQL | Надёжность, JSONB для произвольных структур, транзакции |
| Деплой | Docker + VPS или Cloud Run | Гибкость: для малого бизнеса — VPS за 1 000 рублей, для enterprise — автоскейлинг в облаке |
| Мониторинг | Telegram-алерты + структурированные логи | Клиент узнаёт о проблеме за минуты, а не за дни |
Почему TypeScript, а не Python, Go или Java? Для интеграций критичен I/O: запросы к API, ожидание ответов, параллельная обработка нескольких источников. Node.js с async/await делает это естественно. TypeScript добавляет типизацию — и когда API на той стороне меняет формат ответа, компилятор ловит проблему до деплоя, а не в три часа ночи в продакшене.
Почему не Kafka вместо BullMQ? Kafka — это про миллионы сообщений в секунду, кластеры брокеров, consumer groups. Наши интеграции обрабатывают десятки-сотни событий в минуту. BullMQ + Redis — ровно тот масштаб: надёжно, просто, без overhead’а на администрирование кластера. Если задаче нужна Kafka — значит, она переросла нашу специализацию, и мы честно об этом скажем.
Пример: FlowWow → МойСклад
Чтобы архитектура не была абстракцией — вот как она работает в конкретном проекте. Флористический магазин, заказы с Flowwow, складской учёт в МойСклад. Подробный разбор — в отдельном кейсе, здесь покажем поток данных через три слоя.
Source layer polling’ит Flowwow каждые 30 секунд. Получает список новых заказов. Каждый заказ кладёт в очередь BullMQ. Задача source — только получить данные и поставить в очередь. Никакой бизнес-логики.
Processing layer берёт заказ из очереди. Проверяет остатки в МойСклад по каждой позиции. Если всё в наличии — принимает заказ на Flowwow и создаёт заказ покупателя в МойСклад с привязкой по внешнему ID (идемпотентность). Если товара нет — мгновенно отклоняет, чтобы покупатель не ждал.
Destination layer записывает результат в МойСклад. Если при записи что-то пошло не так — задача возвращается в очередь с exponential backoff. Пять попыток. Если все пять провалились — dead letter queue и алерт в Telegram.
В пиковый день (8 марта) система обработала 330 заказов без единого ручного вмешательства.
Что происходит при сбое
Интеграция работает 24/7, а внешние API — нет. Вот реальные сценарии, которые случаются регулярно, и как система с ними справляется.
Сценарий 1: API недоступен. МойСклад API возвращает 503. Первая попытка — через 3 секунды. Вторая — через 9. К четвёртой попытке (через 81 секунду) API обычно уже вернулся. Заказы, поступившие во время сбоя, копятся в очереди — ни один не теряется. Когда API вернулся, очередь разгребается автоматически.
Сценарий 2: Rate limit. Ozon API вернул 429 — превысили лимит запросов. Система не паникует: задача возвращается в очередь с увеличенной задержкой. BullMQ поддерживает rate limiting из коробки — можно задать «не более 50 запросов в минуту», и библиотека сама выдерживает паузы.
Сценарий 3: Таймаут без ответа. Запрос на создание заказа в МойСклад ушёл, ответ не пришёл за 30 секунд. Создался заказ или нет — неизвестно. Система ставит задачу на retry. При повторной попытке проверяет: есть ли заказ с таким внешним ID? Если есть — задача считается выполненной. Если нет — создаёт заново. Идемпотентность исключает дубликаты.
Сценарий 4: Непредвиденная ошибка. API изменил формат ответа, TypeScript-десериализация сломалась. Задача падает, все пять retry проваливаются с одной и той же ошибкой. Задача уходит в dead letter queue. Telegram-алерт приходит разработчику с полным контекстом: какой заказ, какой endpoint, какая ошибка, сколько попыток было. Разработчик обновляет маппинг, перезапускает задачу из DLQ — заказ обрабатывается.
Главное: ни один сценарий не приводит к потере данных. Задержка — да. Потеря — нет.
Мониторинг: не dashboard, а Telegram
Когда мы только начинали, пробовали стандартный путь: Grafana, дашборды, метрики. Красиво. И абсолютно бесполезно для малого и среднего бизнеса.
Почему: дашборд работает, когда кто-то на него смотрит. В компании с 10-50 сотрудниками нет дежурного инженера, который сидит перед монитором с графиками. Менеджер, который отвечает за интеграцию, сидит в Telegram — переписывается с поставщиками, с клиентами, с командой. Если уведомление приходит туда же — он увидит его через минуту. Если на дашборд — через день. Или никогда.
Наш мониторинг устроен так:
Telegram-алерты при сбоях. Задача попала в dead letter queue, circuit breaker разомкнулся, сервис перезапустился — алерт приходит в Telegram-чат. Формат: что случилось, когда, с какими данными, сколько попыток было. Не «ошибка в системе», а конкретика, по которой можно действовать.
Ежедневная сводка. Раз в день — отчёт: сколько задач обработано, сколько ошибок, среднее время обработки. Позволяет поймать деградацию до того, как она стала проблемой. Если вчера обрабатывали 200 заказов за 2 секунды, а сегодня те же 200 за 15 секунд — что-то изменилось, нужно разобраться.
Watchdog. Мониторинг сам может сломаться. Отдельный процесс проверяет, что основная система жива. Если основной сервис не отчитался за 15 минут — watchdog шлёт алерт. Это не перестраховка — мы убедились в необходимости watchdog’а на практике, когда GAS-триггер молча не срабатывал два дня подряд.
Для enterprise-клиентов добавляем Grafana и structured-логи в ELK. Но для 90% наших проектов Telegram-бот + watchdog покрывает все потребности мониторинга — и клиент уже там.
Итоги
Четыре принципа, которые мы закладываем в каждую интеграцию:
-
Очередь задач — обязательна. Без очереди нет retry, нет персистентности, нет наблюдаемости. Даже если «тут всего десять заказов в день» — очередь нужна. Потому что рано или поздно API упадёт, и без очереди эти десять заказов потеряются.
-
Retry — это система, а не
try/catchс повтором. Exponential backoff, circuit breaker, dead letter queue, разделение retryable и non-retryable ошибок. Каждая ошибка обрабатывается по-разному, каждая — видима. -
Идемпотентность — не опция, а требование. Если операция может быть повторена автоматически, она обязана быть безопасной при повторении. Внешний ID, проверка перед записью, upsert вместо insert.
-
Мониторинг, который реально работает. Telegram-алерты доходят до человека за минуту. Дашборд, на который никто не смотрит, — бесполезен. Watchdog следит за самим мониторингом.
Эти принципы не зависят от стека. Они одинаково работают в BullMQ на Node.js и в самописной очереди на Google Apps Script. Инструменты разные — архитектурные решения одинаковые.
Читайте также
- Flowwow + МойСклад: автоматизация заказов — BullMQ, polling каждые 30 секунд, 330 заказов в пиковый день
- Управление ценами на Ozon: 4 000 SKU из Google Таблицы — Google Apps Script, Ozon Seller API, валидация и промоцены
- Мониторинг 700+ Google Таблиц — GAS, своя очередь, checkpoint-resume, watchdog
- AI-классификация документов — конвейер GAS + Claude API, 200+ документов в день
Если вы выбираете подрядчика для интеграции и хотите понять, как именно будет устроена система — запишитесь на диагностику. Разберём вашу задачу, покажем архитектуру на примере и оценим сроки — до начала работ.
С вами была команда GoogleSheets.ru, мы строим интеграции, которые не теряют данные.
Не хотите разбираться сами?
Если читали статью и поняли, что руками уже не справляетесь — напишите. Оценим задачу бесплатно и предложим решение.
Как автоматизация окупается → Чеклист: 7 процессов для автоматизации →
Похожие статьи

Как мы автоматизировали управление ценами на 4 000 SKU на Ozon через Google Таблицу и сократили время обновления с 3 часов до 5 минут.

Дистрибьютор автозапчастей обновлял каталог 3 дня вручную. Построили конвейер Node.js + Claude API — теперь обновление занимает 1 час, ошибок в 8 раз меньше.

Поисковый движок для ЭТП: продавец описывает товар своими словами — AI находит закупки среди тысяч лотов.
Хотите такой же результат?
Расскажите о задаче — предложим решение и покажем релевантные кейсы.
Написать в TelegramПодписывайтесь — делимся скриптами, кейсами и лайфхаками