PHP зарабатывает деньги. Next.js их показывает.
Когда клиент говорит «хотим headless», первое, что я спрашиваю — а что именно мы переносим?
Обычно имеют в виду: «хотим красивый фронт на Next.js». Никто не имеет в виду: «хотим разобраться, кто отвечает за сбои». А зря. Именно второй вопрос определяет, получится ли архитектура рабочей или она через год превратится в две плохо связанных системы.
Я работаю с headless-проектами на базе Bitrix и Next.js. Один из них — 28 000 SKU, торговля промышленным оборудованием, цены с НДС в рублях, три склада. Расскажу, как мы думаем про разделение слоёв — и где сами ошиблись.
Что значит «зарабатывать»
PHP и Bitrix в этой архитектуре зарабатывают деньги. Не в метафорическом смысле — буквально: каждая запись в iblock — это либо прямой доход (товар, цена, наличие), либо условие, при котором доход возможен (правило скидки, статус заказа, привязка к складу).
В Bitrix живёт всё, от чего зависит сделка:
- каталог: 28 000 SKU, PROPERTY_* поля, цены по группам, единицы измерения
- ценообразование: базовые цены, наценки для дилеров, акционные правила
- складская логика: остатки по трём складам, резервирование, дата ожидаемого поступления
- заказы: корзина, оплата, интеграция с 1С, статусы отгрузки
- CRM: лиды, сделки, история взаимодействий с клиентом
Всё это — результат многолетних бизнес-решений, зашитых в структуру данных. «Переписать» это означает не «перенести» — означает воссоздать каждое из этих правил заново, с нуля. Это не frontend-задача.
Поэтому PHP остаётся. Он зарабатывает.
Что значит «показывать»
Next.js в этой архитектуре ничего не знает про цены. Он делает HTTP-запрос к Bitrix REST API, получает объект с уже рассчитанной ценой, и рендерит цифру на экране.
Зона ответственности Next.js:
- роутинг и SEO-разметка страниц каталога, карточек, категорий
- рендеринг: серверный (SSR), статический с инкрементальной регенерацией (ISR), клиентский для поиска
- UI-компоненты: фильтры, карусели, корзина-виджет (но не логика корзины)
- CDN-доставка: статика через Vercel или собственный CDN, edge caching заголовков
- производительность: Core Web Vitals, LCP ниже 2.5s, минимальный JavaScript на клиенте
Next.js не вычисляет, кому какая скидка. Не знает, есть ли товар на складе — он получает флаг in_stock: true/false из API и показывает «в наличии» или «под заказ». Это не безграмотность Next.js. Это правильное разделение.
Поэтому Next.js показывает. Только показывает.
Три ситуации, где деление спасает
Это звучит абстрактно, пока не разбираешь реальные инциденты.
Bitrix уходит на обслуживание
Bitrix нужно обновление безопасности. Маленький сайт ставит окно обслуживания и закрывается на час. Нам это не подходит — промышленное оборудование, B2B, заказы идут в рабочее время.
Решение: Next.js на ISR. Страницы каталога и карточек товаров сгенерированы и закешированы. Пока Bitrix недоступен, Next.js отдаёт stale-версии. Пользователь видит товары, добавляет в корзину — действие сохраняется в localStorage на фронте. Когда Bitrix поднимается, корзина синхронизируется.
Bitrix лежит 20 минут. Сайт работает. Продажи идут.
Без headless этого не было бы — Bitrix был бы и слоем данных, и слоем рендеринга, и при его падении упало бы всё.
Next.js деплой ломается
Мы переехали на новую версию компонента фильтрации. Тест прошёл в стейджинге, в проде — белый экран у части пользователей. Неприятно.
Но: оформление заказов шло через checkout, который имел прямой URL на Bitrix-бэкенд. Не через Next.js. Пока мы раскатывали hotfix, B2B-клиенты оформляли заказы через прямой URL — менеджер просто давал ссылку в переписке.
Два часа Next.js был сломан. Выручка не упала.
A/B тест на фронте без риска для данных
Нужно проверить гипотезу: карточка товара с видео конвертирует лучше, чем без. Next.js разворачивает два компонента, трафик делится 50/50. Bitrix-данные те же, REST API тот же, никакого кода на бэкенде не меняем.
Тест запускается за день. Откатывается за час. Bitrix не знает, что мы что-то тестировали.
Где деление ломается
Самая частая ошибка: бизнес-логика начинает уходить в Next.js «для удобства» или «для скорости».
У нас это произошло с расчётом скидок. Логика была такая: акционная цена приходит из Bitrix как поле price_sale, а Next.js считает процент скидки самостоятельно (price_sale / price_base * 100). Казалось, это простая математика — зачем гонять запрос к Bitrix ради одной цифры?
Через три месяца Bitrix-менеджер поменял правило округления цен. Базовые цены стали округляться по-другому. Bitrix пересчитал всё корректно. Next.js продолжал считать по старой формуле. Три месяца на части карточек был неправильный процент скидки. Клиенты видели «-23%», хотя реальная скидка была «-19%».
Откат занял день. Урок простой: всё, что имеет денежный смысл, считает Bitrix. Next.js только рендерит результат.
Второй вариант поломки: контент-редактор начинает использовать Next.js-компоненты для хранения данных (например, hardcode цен в JSON-файлах в репозитории). Это превращает деплой Next.js в деплой бизнес-данных. Опасно.
Как это выглядит в проекте
Для 28k SKU у нас получился такой контракт:
12 REST-эндпоинтов на стороне Bitrix. Строгие типы, без бизнес-логики на фронте. Каждый эндпоинт отдаёт уже рассчитанные данные: цены с учётом групп и скидок, остатки по приоритетному складу, флаги доступности.
Деплой Next.js через PM2 — 3 минуты, независимо от Bitrix. Деплой Bitrix — 40 минут по FTP, требует тест окружения. Они работают по разным расписаниям и никогда не блокируют друг друга.
Мониторинг раздельный: health check для Next.js (/api/healthz), health check для Bitrix REST API (/rest/api/health). Разные Telegram-алерты, разные дежурные.
Это скучно. Именно поэтому работает.
Одна строка для принятия решений
Когда не понимаете, куда класть фичу:
Если фича читает или пишет что-то с денежными последствиями — это Bitrix. Если фича только рендерит — это Next.js.
Цена — Bitrix. Остаток — Bitrix. Статус заказа — Bitrix. Баннер на главной — Next.js. Анимация добавления в корзину — Next.js. Форматирование числа 2499 как «2 499 ₽» — Next.js.
Если фича находится на границе (например, корзина с локальным состоянием и синхронизацией) — она требует явного протокола: кто источник правды при конфликте. Ответ здесь однозначный: по данным правду знает Bitrix, по тому, как их показывать — Next.js.
Следовать этому правилу неудобно в краткосрочной перспективе. Через год — спасает.
Подробнее о том, как deploy independence работает на практике — в статье headless-deploy-independence. О контракте REST API для 28k SKU — в bitrix-rest-api-headless-surprises. О кэш-инвалидации при обновлении товаров — в nextjs-isr-bitrix-cache-invalidation.