Переписывать монолит с нуля — затея интересная, но статистически провальная. Много таких проектов не доживают до продакшена. Есть способ лучше: паттерн Strangler Fig, который позволяет постепенно вытаскивать функционал из монолита, не останавливая бизнес.
Сегодня разберём, как это работает на примере Битрикса.
Что такое Strangler Fig
Мартин Фаулер подсмотрел идею у фикуса-душителя — растения, которое обвивает дерево-хозяина и постепенно его замещает. В архитектуре это означает: не выбрасываем монолит, а строим новую систему вокруг него, откусывая функционал кусочками.
Три этапа:
Transform — выносим часть логики в микросервис
Coexist — старое и новое работают параллельно
Eliminate — убираем дублирующийся код из монолита
Главное преимущество — возможность отката на любом этапе. Что-то пошло не так? Возвращаем трафик в монолит, разбираемся спокойно.
Подход хорош для Битрикса, где кодовая база часто представляет собой смесь стандартных модулей, кастомных компонентов и интеграций, накопленных за годы.
Прокси-слой: точка принятия решений
Первое, что нужно — прослойка между клиентами и бэкендом. Она решает, куда направить запрос: в монолит или микросервис.
Простейший вариант на nginx:
map $request_uri $backend { default bitrix_monolith; ~^/api/v2/orders order_service; ~^/api/v2/inventory inventory_service; ~^/api/v2/notifications notification_service; } server { location / { proxy_pass http://$backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
Добавление нового сервиса — одна строчка в конфиге. Откат — удаление этой строчки. Никакого деплоя кода, изменения применяются за секунды через nginx -s reload.
Можно использовать и другие решения: Envoy, Traefik, Kong — принцип тот же. Выбор зависит от того, что уже используется в инфраструктуре.
Битриксовые вебхуки (/rest/{user_id}/{token}/...) на первых этапах лучше оставить в монолите. Трогать их имеет смысл, когда основная миграция завершена.
Входящие вебхуки: вызываем Битрикс из микросервисов
Битрикс24 предоставляет REST API через входящие вебхуки. Создаем вебхук в админке выбираем нужные права, получаем URL вида https://your.bitrix24.ru/rest/1/abc123xyz/.
Дальше дергаем методы, добавляя их к URL: .../crm.deal.list, .../crm.contact.add и так далее.
Пару нюансов:
Rate limiting. Битрикс ограничивает частоту запросов. На бесплатных тарифах около 2 запросов в секунду, на платных лимиты выше, но всё равно есть. Без троттлинга быстро упрётесь в ошибку QUERY_LIMIT_EXCEEDED.
class BitrixClient: def __init__(self, webhook_url: str): self.base_url = webhook_url.rstrip("/") self._last_request = 0.0 def _throttle(self): elapsed = time.monotonic() - self._last_request if elapsed < 0.5: time.sleep(0.5 - elapsed) self._last_request = time.monotonic()
Retry с экспоненциальным backoff. Даже с троттлингом Битрикс иногда возвращает ошибки при пиковой нагрузке. Повторяем запрос с нарастающей задержкой:
def call(self, method: str, params: dict = None): url = f"{self.base_url}/{method}" for attempt in range(3): self._throttle() response = httpx.post(url, json=params or {}, timeout=30) data = response.json() if data.get("error") == "QUERY_LIMIT_EXCEEDED": time.sleep(2 ** attempt) # 1s, 2s, 4s continue if "error" in data: raise BitrixAPIError(data["error"], data.get("error_description")) return data.get("result") raise BitrixAPIError("RETRY_EXHAUSTED", "Max retries exceeded")
Батчинг. Битрикс позволяет отправлять до 50 команд за один HTTP-запрос через метод batch. Обновление 200 сделок займёт 4 запроса вместо 200.
def batch(self, commands: dict) -> dict: """ commands = { "deal1": ("crm.deal.get", {"ID": 1}), "deal2": ("crm.deal.get", {"ID": 2}), } """ cmd = {alias: f"{method}?{urlencode(params)}" for alias, (method, params) in commands.items()} result = self.call("batch", {"halt": 0, "cmd": cmd}) return result.get("result", {})
Параметр halt: 0 означает продолжать выполнение даже при ошибке в одной из команд. Если нужно остановиться при первой ошибке — ставим halt: 1.
Исходящие вебхуку
Когда в Битриксе происходит событие (создали сделку, изменили статус, добавили комментарий), он может отправить POST-запрос на указанный URL. Настраивается в админке: выбираешь событие, указываешь endpoint.
Это позволяет микросервисам реагировать на изменения в реальном времени, не опрашивая Битрикс постоянно.
Пвроблемы, которые нужно решить:
Дубликаты. Битрикс иногда отправляет одно событие дважды — при сетевых проблемах, таймаутах, или просто потому что так получилось. Без дедупликации получите двойные записи.
Решение — хранить обработанные события в Redis с TTL:
@app.post("/webhooks/bitrix/deal") async def handle_deal_webhook(request: Request): body = await request.body() payload = parse_webhook(body) # Дедупликация event_key = f"bitrix:event:{payload.event}:{payload.data['ID']}" if await redis.exists(event_key): return {"status": "duplicate", "message": "Already processed"} await redis.setex(event_key, 3600, "1") # TTL 1 час # Обработка по типу события if payload.event == "ONCRMDEALADD": await handle_deal_created(payload.data) elif payload.event == "ONCRMDEALUPDATE": await handle_deal_updated(payload.data) return {"status": "ok"}
Потери. Если сервис недоступен в момент события — оно потеряно навсегда. Битрикс не повторяет отправку, не ведёт очередь.
Решение — периодическая сверка критичных сущностей. Раз в час (или чаще для важных данных) сравниваем состояние в Битриксе и микросервисе:
async def sync_deals(): last_sync = await redis.get("deals:last_sync") or "2020-01-01" deals = bitrix.call("crm.deal.list", { "filter": {">DATE_MODIFY": last_sync}, "select": ["ID", "STAGE_ID", "OPPORTUNITY"] }) for deal in deals: await process_deal_update(deal) await redis.set("deals:last_sync", datetime.now().isoformat())
Безопасность. Любой, кто узнает URL вебхука, может отправить фейковое событие. Битрикс подписывает запросы — обязательно проверяйте подпись:
def verify_bitrix_signature(request: Request, body: bytes, secret: str) -> bool: signature = request.headers.get("X-Bitrix-Signature", "") expected = hmac.new( secret.encode(), body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected)
Дополнительно можно ограничить IP-адреса, с которых принимаются вебхуки — Битрикс публикует список своих серверов.
Anti-Corruption Layer: изоляция моделей
ACL — прослойка, которая переводит данные между моделями монолита и микросервисов. Без неё код быстро превращается в такую вот кашу из битриксовых полей, разбросанных по всему проекту.
Пример проблемы: в Битриксе статус сделки — строка "PREPARATION", сумма — строка "15000.00", дата — в формате "DD.MM.YYYY". В микросервисе хочется нормальный enum, Decimal и datetime.
Адаптер инкапсулирует все преобразования:
class BitrixDealAdapter: STATUS_MAP = { "NEW": OrderStatus.NEW, "PREPARATION": OrderStatus.PROCESSING, "PREPAYMENT_INVOICE": OrderStatus.AWAITING_PAYMENT, "EXECUTING": OrderStatus.IN_DELIVERY, "WON": OrderStatus.DELIVERED, "LOSE": OrderStatus.CANCELLED, } @classmethod def to_order(cls, deal: dict) -> Order: return Order( id=f"BX-{deal['ID']}", external_id=deal["ID"], status=cls.STATUS_MAP.get(deal["STAGE_ID"], OrderStatus.UNKNOWN), total=Decimal(deal.get("OPPORTUNITY", "0")), created_at=cls._parse_date(deal.get("DATE_CREATE")), customer_id=deal.get("CONTACT_ID"), ) @classmethod def from_order(cls, order: Order) -> dict: reverse_map = {v: k for k, v in cls.STATUS_MAP.items()} return { "STAGE_ID": reverse_map.get(order.status, "NEW"), "OPPORTUNITY": str(order.total), } @staticmethod def _parse_date(date_str: str) -> datetime | None: if not date_str: return None return datetime.strptime(date_str, "%d.%m.%Y %H:%M:%S")
Вся логика преобразования в одном месте.
Бонусом адаптер легко покрывается юнит-тестами отдельно от интеграционных тестов с реальным Битриксом.
Стратегия миграции
Порядок выноса функционала важен. Начинать с ядра бизнес-логики — рискованно: высокая цена ошибки, много зависимостей.
Рекомендуемый порядок:
Аналитика и отчёты. Только чтение данных через batch-запросы. Падение сервиса аналитики не влияет на основной бизнес — просто отчёты показывают данные часовой давности.
Уведомления. Email, SMS, push-уведомления. Вебхуки Битрикса триггерят сервис нотификаций. Потеря уведомления неприятна, но на самом деле не так критично,заказ-то создан и обрабатывается.
Интеграции с внешними системами. Доставка (СДЭК, Boxberry), платёжные системы, склады. Битрикс сообщает о событии, микросервис взаимодействует с внешним API, результат пишется обратно в Битрикс.
Каталог товаров. Синхронизация, кеширование, поиск. Здесь уже двусторонний обмен данными и вопросы консистентности.
Заказы и CRM. Ядро. Браться только когда набита рука на предыдущих этапах и есть уверенность в инфраструктуре.
Каждый этап — отдельный деплой с возможностью отката. Между этапами могут проходить недели или месяцы.
Пару проблем
Два источника правды. Данные есть и в Битриксе, и в микросервисе. Какие актуальные?
Нужно определить «владельца» для каждого типа данных. Например: статусы заказов — микросервис (он знает про доставку), данные клиента — Битрикс (там работают менеджеры).
Таймауты и circuit breaker. Любое решение, вне зависимости от архитектуры, может медленно отвечать при сложных запросах или под нагрузкой.
from circuitbreaker import circuit @circuit(failure_threshold=5, recovery_timeout=60) def call_bitrix(method: str, params: dict): return client.call(method, params)
После 5 ошибок подряд circuit breaker размыкается и сразу возвращает ошибку, не дожидаясь таймаута. Через 60 секунд пробует снова.
Логирование и отладка. Когда что-то идёт не так, нужно понимать, что именно. Логируйте все запросы и ответы, всегда и везде.
Идемпотентность. Операции должны быть безопасны для повторного выполнения. Получили дубль вебхука? Повторили запрос после таймаута? Результат должен быть тот же, что и при первом выполнении.
Итак, основные компоненты интеграции:
Прокси-слой для маршрутизации трафика между монолитом и микросервисами
Входящие вебхуки для вызова Битрикса из микросервисов (с троттлингом, retry и батчингом)
Исходящие вебхуки для получения событий из Битрикса (с дедупликацией и верификацией)
Anti-Corruption Layer для изоляции доменных моделей
Начинайте с периферии (аналитика, уведомления), двигайтесь к ядру постепенно. Каждый этап должен быть обратимым.
Не пытайтесь мигрировать всё за месяц!

Если Битрикс24 для вас — не «админка», а платформа, стоит разложить его по слоям. Курс «Разработчик Битрикс24» про практику: развёртывание, события, модули и REST-приложения, бизнес-процессы и оптимизация — чтобы уверенно работать с legacy и кастомизациями без хаоса.
Чтобы узнать больше о формате обучения и познакомиться с преподавателями, приходите на бесплатные демо-уроки:
9 февраля в 20:00. «Бизнес-процессы в Битрикс24, написание своих кастомных действий». Записаться
19 февраля в 20:00. «Битрикс24 + MAX: разработка чат-ботов и автоматизация коммуникаций». Записаться
