Не трогай, оно интегрировано
Как мы переделали личный кабинет с 1С, Bitrix24 и Oracle Opera PMS — и не построили вторую ERP.
Сразу оговоримся: проект находится под NDA, поэтому я не называю заказчика и опускаю детали, по которым его можно идентифицировать. При этом техническая логика кейса, проблемы и принятые решения сохранены.
На первой встрече никто не говорит: «У нас тут легаси-зоопарк, разберитесь и сделайте, чтобы он не падал». Обычно запрос звучит куда безобиднее: обновить интерфейс, добавить несколько сценариев или подключить еще один сервис.
Так было и здесь. На входе был редизайн личного кабинета крупной организации. Открываем проект, а под старым интерфейсом обнаруживается система, которую годами развивали разные команды. Внутри: десятки сервисов и интеграций, код с неочевидной логикой, дубли и фрагменты, назначение которых уже никто не мог уверенно объяснить.
На словах нужно было обновить экраны. На деле — привести в порядок разрозненный контур, сохранить бизнес-логику и повысить стабильность. При этом никто уже не мог объяснить, почему система устроена именно так.
Редизайн оказался работой с легаси-зоопарком
Личный кабинет уже был связан с Oracle Opera PMS, 1С:Предприятием, Bitrix24, внутренним сервисом оплаты и другими сервисами заказчика. Это был унаследованный контур, а не набор платформ для нового запуска. Пользователь мог оставить заявку, получить счет или закрывающий документ, перейти к оплате. Система была неудобной и местами хрупкой, но обслуживала реальные процессы.
При этом у заявки было только два видимых состояния: «открыта» и «закрыта». Пользователь видел начало и конец, но не понимал, что происходит между ними. Разные группы клиентов попадали в одинаковый интерфейс, хотя пользовались разными услугами. Часть обменов держалась на коде без полной документации. Автора, готового объяснить исходный замысел, тоже не было.
Мы планировали просто обновить интерфейс. После аудита стало понятно: без рефакторинга бэкенда новый UI унаследует прежнюю нестабильность. Поэтому пришлось лезть под капот: разграничить зоны ответственности систем, убрать дублирующую логику и перевести критичные синхронные запросы в асинхронную обработку. Так личный кабинет перестал зависеть от каждого таймаута внешнего сервиса.
Стек и контур проекта
Для этого кейса куда интереснее то, что происходило под капотом:
- 1С-Битрикс (D7) и PHP 8.x;
- хранение агрегированных данных в Highload-блоках Bitrix и работа с ними через ORM;
- двусторонняя асинхронная синхронизация;
- внешние интеграции с Oracle Opera PMS, 1С:Предприятием и Bitrix24 CRM;
- Redis для хранения и быстрой проверки сессионных токенов;
- нативные JavaScript-компоненты с асинхронным обновлением интерфейса.
Но решал здесь не конкретный фреймворк. Главный вопрос был в другом: какую роль личный кабинет вообще должен играть в этой архитектуре.
Почему мы не стали переписывать все с нуля
Переписать все с нуля звучит заманчиво. Ровно до момента, когда выясняется: личный кабинет не хранит мастер-данные и не должен становиться второй ERP. По сути, это BFF (Backend for Frontend). Кабинет собирает данные из нескольких внутренних контуров, готовит их под конкретный сценарий и отдает интерфейсу в удобном виде.
Бронирования, заявки, платежи, документы и CRM-процессы уже жили в профильных системах заказчика. Перенести все это в кабинет означало бы собрать еще один монолит и продублировать рабочие правила. В результате появились бы несколько версий правды и вечный вопрос: какая из них сегодня правильная?
Поэтому перед каждой доработкой мы спрашивали: где на самом деле живет логика этого процесса? Ссылку на оплату формирует внутренний сервис. Кабинет только вызывает его и показывает результат. Документы приходят из 1С. Кабинет хранит локальное представление для быстрого доступа, но не становится их источником. Статус меняется в Bitrix24. Значит, системы должны синхронизироваться, а не рассказывать пользователю две разные истории.
Границу провели просто: внутренние системы отвечают за бизнес-правила, а личный кабинет собирает данные и ведет пользователя по нужному сценарию.
Как устроили обмен между системами
Данные из 1С поступали в веб-приложение в прежнем формате и сохранялись локально. Для быстрого чтения мы использовали Highload-блоки Bitrix. Интерфейс рендерил уже подготовленные данные из локального хранилища и не зависел от ответа 1С при каждом открытии страницы.
Со статусами началась настоящая боль. Нарисовать еще несколько плашек было легко. Но тогда кабинет и Bitrix24 быстро начали бы показывать разные версии одного процесса. Поэтому вместо поверхностной доработки мы построили двустороннюю асинхронную синхронизацию по двум потокам:
- из Bitrix24 изменения приходят на выделенные эндпоинты кабинета через исходящие вебхуки CRM;
- из личного кабинета данные отправляются в Bitrix24 по REST API через обертку CRest, а фоновые агенты по расписанию сверяют состояние.
Двусторонний обмен быстро сталкивается с конфликтами состояний, гонками запросов и внезапно пропавшей сетью. Поэтому мало доставить событие «прямо сейчас». Нужна последующая сверка. Если одна сторона была недоступна, системы все равно должны сойтись к рабочему состоянию.
На той же связке построили согласование документов. Менеджер прикрепляет файл в Bitrix24, предложение согласовать его появляется в кабинете, а ответ клиента возвращается в CRM. Пользователю не нужно искать контекст в почте или мессенджере, а менеджер продолжает работать в своей системе.
Три грабли, на которые мы наступили
1. HTTP 200 от 1С, но актов в кабинете нет
Часть актов просто не доезжала до личного кабинета. На стороне 1С выгрузку проверяли — все штатно. И формально это было правдой: сервер честно возвращал HTTP 200.
Но HTTP 200 еще не делает XML корректным. Мы хотели увидеть ответ до того, как за него возьмется парсер. Поэтому добавили жесткое логирование сырых payload прямо в обертку импорта. Каждый ответ от 1С сохранялся до разбора как сырой XML. Первые узлы массива выводили отдельно, чтобы быстро проверить структуру.
Так и нашли причину: статус успешный, но в XML периодически не хватало обязательных узлов. После парсинга часть контекста терялась. Без сырого дампа мы бы еще долго выясняли, на чьей стороне проблема.
А если 1С пришлет пять гигабайт и положит диск?
Здесь любой технарь спросит: что будет, если 1С пришлет пять гигабайт за час и забьет диск? При неограниченных дампах именно это и произойдет. Но нам нужно было поймать ошибку за два часа, а не неделю разворачивать ELK-стек ради одного инцидента.
Поэтому решение сделали временным и контролируемым: ограничили размер дампа и настроили жесткую ротацию. Ошибку поймали, а срочная диагностика не превратилась в отдельный инфраструктурный проект.
2. Регистрация падала, когда CRM задумывалась
Первая версия регистрации была прямолинейной и хрупкой. Пользователь заполнял форму, а кабинет вызывал REST API CRM и ждал ответ. Только после этого клиент получал доступ. Стоило балансировщику или CRM задуматься на пару секунд, регистрация падала по таймауту.
Мы убрали CRM из критического пути. Теперь локальный пользователь создается нативными средствами движка и сразу попадает в кабинет. А отдельный фоновый воркер по cron раз в несколько минут собирает новых пользователей пачкой и отправляет их в Bitrix24.
Лид появляется с небольшой задержкой, зато доступ клиента больше не зависит от доступности внешней сети. Для обработки ввели статусы «успех» и «ошибка», чтобы повторные запуски не зацикливались на проблемных данных и их можно было разбирать отдельно.
Почему cron, если есть Kafka и RabbitMQ?
На архитектурной схеме RabbitMQ, конечно, выглядел бы солиднее. Но очереди полезны там, где они уже развернуты и обслуживаются. Ради одной функции пришлось бы добавить новый критический компонент, а вместе с ним мониторинг и поддержку. Получился бы тот самый выстрел из пушки по воробьям.
В наших условиях простой cron с пакетной обработкой и явными статусами оказался надежнее лишней инфраструктуры. Это не универсальный рецепт, а нормальный инженерный компромисс при приемлемой задержке синхронизации.
3. Сессии терялись между сервисами, а поиск контрагентов тормозил API
Единое API для мобильного приложения быстро показало слабое место старой авторизации. Между сервисами терялись сессии, а проверка токена при каждом запросе создавала лишнюю нагрузку на реляционную базу.
Точечным ремонтом здесь было не обойтись, поэтому механизм выдачи токенов переписали. Сессионные ключи вынесли из MySQL в Redis, задали жесткий TTL и убрали SQL-запрос из каждой проверки API.
Параллельно убрали тяжелый поиск контрагентов. Раньше пользователей сопоставляли по ИНН через сложные JOIN-запросы к основной таблице. Справочник вынесли в легковесные Highload-блоки и обращались к нему напрямую через ORM. Время ответа API сократилось в разы. Мобильное приложение перестало терять сессии при обновлениях.
Почему Redis, а не JWT?
Логичный вопрос: почему не JWT? Он хорошо снимает необходимость хранить сессию на сервере, но мгновенно отозвать токен до истечения срока действия уже сложнее. А нам было важно закрывать сессии сразу и не нагружать MySQL постоянными проверками.
Redis дал быструю валидацию и централизованный отзыв. Не потому, что JWT «плохой». В этом проекте управляемость сессий оказалась важнее stateless-модели.
Один кабинет — разные пользовательские сценарии
Кабинетом пользуются разные группы клиентов с разными типами договоров. Мы настроили роутинг: после авторизации пользователь видит только те услуги, опросы и документы, которые относятся к его группе, например ретейл или арендаторы.
Мы изменили регистрацию и авторизацию так, чтобы пользователь сразу попадал в нужный сценарий. Для отдельных групп появились свои страницы и базы знаний. Главный экран тоже зависит от группы: на нем выводятся популярные услуги, мгновенные опросы и последние счета или закрывающие документы. Подборку услуг и аудиторию опросов можно настраивать отдельно.
При этом сами услуги и заявки по-прежнему создаются во внутреннем конструкторе заказчика средствами Bitrix. Личный кабинет показывает карточки в новом дизайне, поддерживает бонусную программу и связывает действия пользователя с существующей логикой — но не копирует ее к себе.
Что получилось
В итоге получился далеко не просто новый интерфейс. Мы:
- расширили статусную модель и связали ее с Bitrix24;
- добавили согласование документов внутри заявки;
- сохранили существующую логику 1С и изолировали данные для быстрого локального чтения;
- развязали регистрацию и доступность CRM;
- перенесли сессионные токены в Redis и ускорили поиск контрагентов через Highload-блоки;
- сделали интеграционные сбои наблюдаемыми и восстановимыми;
- дали разным группам клиентов отдельные пользовательские сценарии.
Самым сложным оказался не новый экран и не очередной API. Нужно было удержать границы огромного легаси-контура: связей много, документации мало. Человека, который точно знает, как все должно работать, уже нет.
Мы не стали строить внутри кабинета вторую ERP и заново изобретать бронирования, заявки, оплаты и документы. Мастер-логику оставили там, где она уже жила, а кабинет сделали устойчивым слоем агрегации и пользовательского взаимодействия.
В таких проектах качество решения видно не по объему переписанного кода. Хороший результат — когда сеть моргнула или внешний сервис задумался, а система восстановилась. Данные сошлись, пользователь ничего не заметил, новое не разрушило бизнес-логику, которую годами собирали до вас.
А у вас где проходит граница между развитием легаси и полным переписыванием? Особенно интересно послушать истории, которые начинались со слов: «Там всего одна маленькая интеграция».
#разработка #интеграции #легаси #архитектура #1СБитрикс #backend #API #редизайн #вебразработка #автоматизациябизнеса