Skip to main content
Статус: принято.

Контекст

У Omniflow две веб-аудитории, у которых почти нет ничего общего. Клиент смотрит, сколько дней осталось, и покупает продление. Оператор возвращает платёж, разбирает совпадение по списку блокировок и правит версию тарифа. Очевидная структура — два фронтенд-проекта, и она была отвергнута. Два проекта — это два конвейера сборки, два набора зависимостей, которые нужно держать на одной версии React, два шлюза доступности, две настройки локализации и — самое дорогое — две системы компонентов, которые начинаются одинаковыми и расходятся. Кнопка, которая не совсем та же кнопка, — мелочь; диалог подтверждения, который не совсем тот же диалог подтверждения, — это то, как разрушительное действие оказывается с более слабой защитой на одной поверхности, чем на другой.

Решение

Одно приложение Next.js в apps/web содержит клиентские маршруты и /admin. Обе поверхности используют одни и те же примитивы shadcn из packages/ui, одни и те же каталоги next-intl на русском и английском, один и тот же слой данных SWR и один и тот же стек форм и валидации. Они разделены ровно там, где разделение является свойством безопасности, и больше нигде:
  • Разные префиксы API. Клиентская панель вызывает /v1/account, операторская — /v1/panel. Ни одна не дотягивается до маршрутов другой, как требует запись 0003.
  • Разные сессии с разным сроком жизни. Операторские истекают после минут бездействия и часов в абсолютном исчислении; клиентские живут днями, потому что повторный вход стоит оператору секунд рабочего дня, а клиент, которого разлогинило за обедом, просто вернётся в бот. Построение токена общее, чтобы реализация и дисциплина хранения были одни; применяемая к нему политика — нет.
  • Разные модели авторизации. Операторская поверхность закрыта RBAC на серверной границе, поэтому оператор без права доступа не получает саму страницу. У клиентской поверхности ролей нет вовсе — она отдаёт только собственные данные клиента.

Последствия

Изменение оформления попадает сразу на обе поверхности — в этом и смысл, — и это же означает, что неосторожное изменение попадает сразу на обе. Общие примитивы и рецензируются как общие: правка контраста, затемнившая токены subtle, success и warning, должна была пройти шлюз доступности и на уже существовавших операторских страницах, а не только на новых клиентских. Между аудиториями не разделяется бандл в том смысле, за который платит клиент: разбиение кода по маршрутам держит операторские экраны вне первой загрузки клиента, а бюджет производительности JavaScript закреплён по маршрутам с потолком на область, а не одним общим числом. Обе поверхности живут в одном развёртывании. Выпустить исправление клиентской панели, не выпустив операторскую той же версии, нельзя; это следует из записи 0001 и является тем же компромиссом.

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

  • Поверхности требуется фреймворк, модель рендеринга или ритм выпуска, которые не подходят другой. Желание другого внешнего вида — не тот случай: система компонентов уже параметризована.
  • Операторская панель обзаводится по-настоящему офлайновым или десктопным режимом, а это другой продукт, а не другой маршрут.

Смежное