Часто слышу в IT:

  • PHP устарел

  • Bitrix — монолит из 2000-х

  • На Bitrix невозможно писать нормальную архитектуру

С Bitrix24 работаю больше шести лет и за это время поработал над проектами: от простых внедрений до CRM с серьёзной нагрузкой и десятками интеграций. Каждый раз убеждался в том, что архитектуру делает не платформа, а разработчик.

Если взять Laravel или Symfony, писать чистый код там намного проще. В этих фреймворках много встроенных архитектурных ориентиров: DI-контейнер, события, middleware, чёткое разделение слоёв, тестируемость «из коробки». Фреймворк определяет границы для разработчика.

В Bitrix меньше границ, больше свободы, можно быстро решить задачу. Повесить обработчик, добавить автоматизацию, дописать скрипт в init.php и функционал будет работать. Но эта свобода несёт за собой риск ошибок, проще написать не поддерживаемый, не расширяемый продукт. Проблема в том, что архитектурные best practice не предлагаются и приходится закладывать самостоятельно. Поэтому проектировать функционал в Bitrix нужно осознаннее и глубже.

В этой статье разберу кейс, где вместо хаотичных CRM-автоматизаций вынес процесс в отдельный workflow-сервис и выстроил управляемую систему поверх Bitrix24.

Задача

Реализовать дашборд управления документами клиента с требованиями:

  • показывать обязательные документы в зависимости от типа клиента

  • отображать статус каждого документа

  • учитывать роли сотрудников

  • реагировать на изменение стадии сделки

  • контролировать SLA

  • масштабироваться на большое количество клиентов

На первый взгляд это собирается внутри CRM. Типичное решение состояло бы из дополнительных пользовательских полей, новых статусов, автоматизаций и бизнес-процессов. В момент запуска это работает, процесс кажется управляемым, логика - понятной. Проблемы начинаются позже, когда добавляются новые типы клиентов и появляются исключения, когда SLA считают не «примерно». 

Через полгода такая конструкция начинает расползаться, и поддерживать становится сложнее:

  • «стадия не соответствует состоянию»

  • «SLA считается в Excel»

  • «часть логики живёт в роботе №7, о котором никто не помнит»

Перегрузка CRM бизнес-логикой

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

  • параллельные направления работы с одним клиентом

  • независимые статусы

  • автоматические переходы

  • SLA-контроль

  • интеграции

  • аудит изменений и отчётность

В этот момент CRM начинает выполнять роль workflow-движка, при том что архитектурно к этому не готова. Логика размазывается между роботами, бизнес-процессами и кодом. Со временем система начинает терять прозрачность, а изменения становятся рискованными, масштабирование превращается в постоянную борьбу с побочными эффектами. Каждое новое правило усиливает связность системы. Каждое изменение требует проверки десятка автоматизаций, логика начинает жить вне единой модели и расползаться по настройкам.

Решение: разделение ответственности

CRM остаётся системой учёта: клиент, сделки, сотрудники, интерфейс для работы. CRM больше не управляет процессом, но отображает состояние, а сам процесс живёт отдельно. CRM остаётся системой учёта и точкой входа.

Workflow Service становится местом, где принимаются решения. Он управляет статусами, проверяет переходы, хранит историю и генерирует события.

Архитектура

  1. CRM хранит карточку клиента, даёт пользователю точку входа в процесс, отправляет вебхуки или команды во внешний сервис

  2. Workflow Service управляет статусами, пишет историю, генерирует доменные события. Изменения статусов проходят через state machine

  3. Database хранит workflow items, справочные данные, mapping к CRM-сущностям

  4. Message Broker публикует доменные события

  5. Automation Layer: Scheduler для SLA и отложенных действий, Rules Engine, Integration Adapters

Как работает поток

Типовой сценарий выглядит так:

  1. Пользователь инициирует изменение статуса в CRM.

  2. CRM отправляет команду в Workflow Service.

  3. Сервис проверяет переход через state machine.

  4. Если переход разрешён, состояние меняется.

  5. Изменение фиксируется в истории и записывается событие в outbox.

  6. Событие публикуется в брокер.

  7. Consumer выполняет действие: создаёт задачи в CRM, обновляет стадию сделки, отправляет уведомления или запускает SLA-таймер.

Таким образом автоматизация строится вокруг событий, а не вокруг скрытых скриптов и роботов.

Почему здесь нужен Outbox

Если брокер недоступен или сервис упал между изменением состояния и отправкой события, есть риск потерять данные, получить рассинхронизацию, что критично для SLA и интеграций. При использовании Outbox-паттерна изменение состояния и создание события происходят атомарно.

$connection->startTransaction();

try {
    // 1. Меняем состояние
    $workflow->changeStatus($targetStatus);
    $workflowRepository->save($workflow);

    // 2. Фиксируем доменное событие
    $outboxRepository->add(new OutboxEvent(
        aggregateId: $workflow->getId(),
        eventType: 'DocumentStatusChanged',
        payload: json_encode([
            'from' => $previousStatus,
            'to'   => $targetStatus,
        ])
    ));

    $connection->commitTransaction();

} catch (\Throwable $e) {
    $connection->rollbackTransaction();
    throw $e;
}

Если транзакция откатилась, состояние не изменяется и событие не создаётся. Отдельный воркер читает таблицу outbox_events и публикует сообщения в брокер. События запускают создание задач в CRM, автоматическое изменение стадий, SLA-таймеры и интеграции. Если событие потеряется, процесс «сломается тихо», т.е. CRM покажет одно состояние и автоматизация не выполнится.

Outbox делает систему предсказуемой, и даже если брокер временно недоступен, состояние не рассинхронизируется.

Почему не внутри CRM

  1. Нет явной state machine. В CRM статус - просто поле, изменяемое разными способами, за ним нет строгой модели переходов

  2. Нет полноценной событийной модели. Здесь нет outbox-паттерна, нет гарантированной доставки событий, нет встроенной идемпотентности и контроля повторной обработки.

  3. Логика размазывается. Часть правил живёт в роботах, часть — в бизнес-процессах, часть — в пользовательских действиях, часть — в коде.

  4. Масштабирование усложняется. Когда нагрузка растёт и правил становится больше, автоматизации начинают конфликтовать, дебаг превращается в расследование, интеграции становятся чувствительными к любым изменениям.

Even-driven подход создаёт явную модель состояний, которой можно централизованно управлять. Недопустимые переходы здесь невозможны. Каждое изменение состояний фиксируются как события, по каждому сохраняется история для SLA и аналитики. Автоматизация прозрачна и реагирует на события. Чтобы добавить новое правило, достаточно добавить consumer. Ещё один плюс - независимость от внешних сервисов.

Вывод

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

Event-driven подход в этом случае даёт понятные преимущества:

  • состояния управляются строго и централизованно

  • автоматизация строится вокруг событий, а не вокруг скрытых скриптов

  • SLA становится частью модели, а не набором костылей

  • систему можно масштабировать независимо от CRM

  • процесс остаётся прозрачным и управляемым

Проблема не в Bitrix, а в смешивании ролей. Важно, где живёт логика и кто ею управляет.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Bitrix — это…
41.67%Боль5
16.67%Компромисс2
16.67%Инструмент2
8.33%Вызов 😄1
16.67%Легаси2
Проголосовали 12 пользователей. Воздержались 2 пользователя.