Контекст
У 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 и является тем же компромиссом.Когда пересматривать
- Поверхности требуется фреймворк, модель рендеринга или ритм выпуска, которые не подходят другой. Желание другого внешнего вида — не тот случай: система компонентов уже параметризована.
- Операторская панель обзаводится по-настоящему офлайновым или десктопным режимом, а это другой продукт, а не другой маршрут.
Смежное
- Панель оператора — операторская поверхность и её граница RBAC.
- Клиентская веб-панель — что клиент может сделать в браузере.
- Запись 0003 — три префикса и общий транспорт.