Логотипbitrix-code
Разработка

D7 ORM против старого API: как безболезненно мигрировать legacy-код Битрикс24

Дмитрий Горохов8 мин

Строки GetList(), Add() и CIBlockElement всё ещё кормят тысячи коробочных Битрикс24. Код работает, но разработчики боятся в него лезть, а каждый новый разработчик задаёт один и тот же вопрос: «а почему не D7?». Плавный переход с legacy-кода на D7 ORM — это не про «переписать всё за раз», а про выгоду, которую можно получать инкрементально, без остановки портала. В этой статье разбираем, почему D7 orm Битрикс24 стал стандартом качества, где старый API ещё оправдан и как мигрировать без инцидентов.

Писать будем для тех, кто принимает решения: CTO, тимлидов и ведущих разработчиков, которым нужны не абстрактные «лучшие практики», а цифры, риски и последовательность действий.

Материал основан на реальных проектах нашей команды: в одном из них мы за четыре месяца перевели на D7-ORM пять самых «горячих» модулей портала на 1 200 пользователей — без единого окна простоя. Ниже — пошаговая методика, типичные ошибки и примеры «до/после» с замерами.

Содержание

Чем D7 лучше GetList: сравнение подходов

Старый API — это процедурные функции, строковые поля и магия в префиксах (ACTIVE_DATE, !ID). D7 — объектный слой поверх тех же таблиц: сущности (DataManager), типизированные поля, чейнинг запросов и единая точка контроля.

КритерийСтарый API (GetList / CIBlockElement)D7 ORM (Entity / DataManager)
ТипизацияСтроковые ключи массива, легко опечататьсяСтрогие классы полей, автодополнение в IDE
КешированиеРучное через nPageSize/кеш-бэкенд, непоследовательноЕдиный кеш через табличные классы, правила в одном месте
ЗапросыФильтр-массивы без композицииЧейнинг: select→where→order→limit→fetch
События и расширяемостьГлобальные хуки с строковыми именамиКлассовые события и валидация на уровне сущности
СовместимостьDeprecated-ветки, поведение меняется между версиямиСтабильный контракт, покрыт документацией 1С-Битрикс

Практический выигрыш — скорость разработки и сопровождаемость: в нашем опыте новый разработчик начинает править D7-код в два-три раза быстрее, чем legacy-модуль.

Отдельный плюс — предсказуемость запросов. В GetList легко незаметно получить выборку со всеми свойствами и макросами; в D7 вы явно указываете, что читаете, и это видно в код-ревью, а не в slow query log.

Почему вообще стоит мигрировать

Три аргумента, которые слышит CTO:

  1. Производительность. Табличный кеш D7 и отказ от избыточных JOIN позволяют ускорить типовые выборки на 30–60% — замер ниже.
  2. Обновляемость. Строки старого API всё чаще помечаются deprecated; на новых версиях коробки часть сценариев ломается тихо.
  3. Найм и онбординг. Рынок знает D7; костыли из 2012 года — нет.

Когда мигрировать не стоит

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

Если портал растёт и нуждается в кастомных модулях, посмотрите на услугу разработки модулей — там где речь про сопровождение, D7 считаем обязательным стандартом.

Типичные ошибки при переходе на D7 ORM

Переписать «всё и сразу»

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

Игнорировать семантику фильтров

В старом API пустое значение в фильтре часто «проглатывается»; в D7 пустая строка — валидное значение и попадёт в WHERE. Не проверив 10-критерийные фильтры, вы получите выборки, «случайно» расширившиеся на все записи.

Потерять «магию» макросов

PROPERTY_123 и IBLOCK_ID из старого API не мигрируют один-в-один. Свойства инфоблоков в D7 живут в отдельных сущностях — их нужно явно джойнить, иначе выборка молча вернёт пустые поля.

Забыть про события и права доступа

Старые обработчики OnAfterIBlockElementAdd срабатывают даже при записи через новый API, а вот кастомные хуки модуля — нет. Карту событий проверяем до, а не после релиза.

Поэтапный рефакторинг без остановки портала

Методика, отработанная на миграциях коробочного Битрикс24 (см. внедрение коробочной версии):

  1. Инвентаризация. Ищем все вызовы GetList/Add/Update по проекту, классифицируем: готовое ядро / аналог в D7 / нужна своя таблица.
  2. Выбираем пилот. Один модуль с высоким трафиком чтения и низким — записи.
  3. Пишем параллельный слой. Новые методы на ORM, старые помечаем @deprecated, но не удаляем.
  4. Сравнительный прогон. Включаем теневой режим: результаты старого и нового кода сравниваются и логируются на расхождения.
  5. Переключение и наблюдение. Меняем вызов в конфиге/фиче-тогле, держим 1–2 недели релиз-мониторинга.
  6. Удаление legacy. Следующий релизный цикл, после подтверждения стабильности.

Ключевой приём — feature toggle: одна константа или настройка модуля откатывает поведение за минуту, без реверта кода.

Из практики: на портале с 1 200 пользователей мы переводили отчётный модуль по ночам, включая новый слой на 10% трафика, потом на 50%, и только потом полностью. Весь переход занял три недели, пользователи ничего не заметили.

Примеры «до/после» с замерами

Выборка 5 000 элементов инфоблока с двумя свойствами, коробка на PHP 8.2, MySQL 8:

// Было: старый API
$res = CIBlockElement::GetList(
    ['DATE_CREATE' => 'DESC'],
    ['IBLOCK_ID' => 14, 'ACTIVE' => 'Y'],
    false, ['nTopCount' => 5000]
);
while ($el = $res->GetNext()) { /* ... */ }

// Стало: D7 ORM
use Bitrix\Iblock\Elements\ElementDealsTable;
$rows = ElementDealsTable::query()
    ->setSelect(['ID', 'NAME', 'DATE_CREATE'])
    ->addFilter('ACTIVE', 'Y')
    ->setOrder(['DATE_CREATE' => 'DESC'])
    ->setLimit(5000)
    ->fetchAll();

Замеры на тестовом контуре с прогревом кеша:

  • Замер кода «до»: 1 240 мс среднее из 100 прогонов.
  • Замер кода «после»: 610 мс среднее, кеш-хит — 90 мс.

В одном из наших проектов аналогичный перевод шести отчётов снизил нагрузку на базу на 40% и убрал таймауты в часы пиковых отчётов.

Что меняется для записи

// Старый API: добавление элемента
$el = new CIBlockElement();
$id = $el->Add($fields); // false при ошибке, текст в $el->LAST_ERROR

// D7: явный результат и валидация
$result = ElementDealsTable::add($fields);
if (!$result->isSuccess()) {
    $errors = $result->getErrorMessages();
}

Главное здесь не скорость, а контроль: ошибки возвращаются объектом, валидация описывается в классе сущности, а поведение не зависит от побочных эффектов глобальных переменных.

Как тестировать и мониторить миграцию

Миграция — это фактически замена движка на лету, поэтому тестирование здесь важнее самого рефакторинга. Минимальный набор:

Чек-лист перед каждым переключением модуля:

  • Юнит-тесты на оба набора фильтров: граничные значения, пустые строки, UTF-8-символы.
  • Сравнение результатов старого и нового кода на продовой выгрузке данных (по 10 000 записей на сущность).
  • Проверка прав доступа: выборка «под сотрудником», «под админом», «аноним».
  • Логи slow query включены на время пилота.
  • План отката: фиче-тогл и сохранённый legacy-код до конца релизного цикла.

На длинных миграциях полезно смотреть в смежные темы производительности — например, оптимизацию MySQL под Битрикс24, чтобы ORM-слой не маскировал проблемы схемы базы.

После переключения держим «неделю тишины»: алерт на slow query, ошибки PHP и расхождения в сверочных логах. Только после неё переходим к чистке legacy-кода.

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

Вывод

  • D7 orm Битрикс24 — это зрелый стандарт: типизация, кеширование и сопровождаемость выигрывают у GetList почти по всем критериям.
  • Мигрировать нужно вертикальными срезами модулями, с теневым прогоном и фиче-тоглом — остановка портала недопустима.
  • Типовые ловушки: семантика пустых фильтров, свойства инфоблоков, события — проверяются на пилоте, а не на проде.
  • Ожидаемый эффект от перевода «горячих» выборок — ускорение в 1,5–2 раза и снижение нагрузки на MySQL на 30–40% в реальных проектах.
  • Устаревший код удаляем только после релизного цикла стабильной работы нового слоя.

Если в вашем портале накопилось legacy, которое тормозит развитие, напишите нам — проведём аудит модулей и предложим план миграции с оценкой по этапам.

FAQ

Обязательно ли переписывать всё на D7 ORM?

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

Старый API перестанут поддерживать в коробке?

Формально он объявлен legacy ещё несколько мажорных версий назад. 1С-Битрикс сохраняет обратную совместимость, но новые возможности (события, валидация, производительность) появляются в D7, а в старом слое фиксируются только баги.

Что делать, если модуль тесно завязан на старые события?

Оставляем обработчики на месте: D7-записи через таблицы инфоблоков по-прежнему дергают OnAfterIBlockElementAdd. Кастомные хуки переносим на классовые события по мере перевода модуля.

Как выбрать, с какого модуля начинать?

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

Сколько занимает такая миграция?

В наших проектах: пилотный модуль — 2–3 недели, портал со 100–200 кастомными вызовами GetList — 3–5 месяцев поэтапно. Точная оценка — после инвентаризации, которую делаем за 3–5 дней.

Можно ли использовать D7 в облачном Битрикс24?

Нет, D7-разработка — прерогатива коробочной версии, где есть доступ к файловой системе и собственным модулям. В облаке кастомизация идёт через REST и локальные приложения; для таких задач смотрите внедрение облачного портала. Если вы на коробке и думаете о переезде — вопрос миграции кода встанет отдельно.