Инженерный кейс

Миграция Bitrix24 → Twenty CRM без потери данных

Самостоятельная миграция CRM с облачного SaaS на self-hosted решение. 935 строк PHP-адаптера, 9 GraphQL-мутаций, dual-write с авто-fallback, дедупликация лидов.

935
строк адаптера
9
GraphQL мутаций
6
файлов интегрировано
29
лидов мигрировано
PHP 8.2 Twenty CRM Bitrix24 GraphQL Docker
Архитектура dual-write
crm_adapter.php
CRM_MODE = dual
Twenty
Bitrix24
GraphQL API
Person → Opportunity → Note
REST API (webhook)
авто-fallback при недоступности
Twenty: active Bitrix24: standby

Почему мы ушли с Bitrix24

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

Что не устраивало

  1. Никаких self-hosted инсталляций API. Все webhook'и привязаны к облаку 1С-Битрикс. Ты не контролируешь ни данные, ни доступ.
  2. API — legacy REST. Не GraphQL. Каждый вызов — отдельный HTTP round-trip. Для создания сущности с кастомными полями — 3 запроса минимум.
  3. Кастомные поля через UF_CR_*. Документация — устаревшая вики на русском. Типы данных ограничены.
  4. Вендор-лок. Переехать с Bitrix24 сложнее чем начать с нуля. Это осознанный vendor lock-in.
После 3 месяцев эксплуатации стало понятно: нам нужна CRM которую мы контролируем. Данные, API, бэкапы, доступы — всё должно быть наше.

Почему выбрали Twenty CRM

Twenty — open-source CRM на NestJS + TypeORM + GraphQL. Не стартап на venture capital, а проект с MIT-лицензией. Для нас — идеальный кандидат на замену:

Архитектура crm_adapter.php

Вместо переписывания всех 6 PHP-файлов сайта — написали один слой абстракции crm_adapter.php (935 строк). Он заменяет все вызовы Bitrix24 на GraphQL-запросы к Twenty.

9 транслированных методов Bitrix24

Адаптер транслирует методы Bitrix24 в GraphQL-мутации Twenty:

Bitrix24 метод Twenty мутация
crm.lead.addcreatePerson + createOpportunity
crm.lead.updateupdateOnePerson
crm.deal.addcreateOpportunity
crm.deal.updateupdateOneOpportunity
crm.timeline.comment.addcreateNote + createNoteTarget
tasks.task.addcreateTask + createTaskTarget
crm.duplicate.findbycommGraphQL findPeople query
im.notifyemail fallback (нет аналога)
bizproc.workflow.startskip (нет аналога)

Режим dual-write

Ключевая особенность — CRM_MODE = dual. Каждая заявка пишется одновременно в обе CRM:

// crm_adapter.php — основная логика dual-write
function crm_create_person($data) {
    $twenty_success = twenty_create_person($data);
    $b24_success    = b24_create_lead($data);       // fallback

    // Если Twenty упал — лид всё равно создан в Bitrix24
    return $twenty_success || $b24_success;
}

Зачем dual-write а не сразу только Twenty? Потому что миграция CRM — это не переключение toggle. Должен быть период когда обе системы работают параллельно. Если Twenty упал (туннель отвалился, Docker перезагрузился) — лиды всё равно попадают в Bitrix24.

Инфраструктура: Docker + туннель

Twenty работает локально в Docker на Windows-машине. Доступ из интернета — через туннель:

Когда Twenty стабильно отработает месяц в dual-write, переключим CRM_MODE на twenty и отключим Bitrix24.

Миграция исторических данных

Перенос 45 лидов и 50 контактов из Bitrix24 в Twenty — скрипт на Python (migrate.py). Экспорт из Bitrix24 в JSON, трансформация полей, создание через GraphQL API.

Что дальше

  1. Отключение Bitrix24. После месяца стабильного dual-write — переключаем CRM_MODE на twenty.
  2. Custom fields. Перенос UF_CRM_BL_INDUSTRY, UF_CRM_BL_BUDGET, UF_CRM_LEAD_SCORE из Bitrix24 в Twenty.
  3. Pipeline (воронка). 10 кастомных этапов вместо стандартных.
  4. Деплой на VPS. Перенос с локального Docker на выделенный сервер для продакшена.
  5. Telegram-уведомления. Замена email-алертов на Telegram-бота для оператора чата.

Главный урок: миграция CRM — это не техническая проблема, а проблема надёжности. Ты не можешь позволить себе потерять ни одного лида во время перехода. Dual-write решает это ценой дублирования данных на переходный период.

Нужна миграция CRM?

Спроектируем адаптер под вашу CRM, настроим dual-write и мигрируем данные без потерь.