Как перенести существующий SaaS в supastack?
Зависит от кодовой базы, но последовательность неизменна: переносите поэтапно, никогда — одним переписыванием. Проведите инвентаризацию репозитория, разделив его на продуктовый код (роуты, схема, доменная логика — он остаётся вашим) и инфраструктуру (аутентификация, тенантность, биллинг, задачи, почта — её заменяет кит). Подключайте модули в порядке зависимостей: структура, аутентификация, воркспейсы, биллинг, затем фоновая работа. Переносите данные перезапускаемым скриптом, сохраняйте ID клиентов Stripe и переключайтесь после прохождения приёмки. Большинство веб-приложений занимает дни вечерами, а не недели.
Проведите инвентаризацию, прежде чем трогать код
Каждая неудачная миграция начиналась одинаково: кто-то открывал репозиторий кита и начинал удалять файлы. Кит — это фундамент, а не тема: он заменяет вашу инфраструктуру и ничего не решает за ваш продукт. Прежде чем что-либо переносить, честно разделите репозиторий на две кучи.
Продуктовый код — это всё, что клиент смог бы пересказать вам: ваши роуты, схема, доменная логика, тексты, страница цен. Инфраструктура — это всё, чего они никогда не видят: сессии, проверки тенантности, платёжные вебхуки, задачи в очереди, транзакционная почта. Первая куча переезжает нетронутой. Вторая заменяется — в этом и весь смысл.
- Выпишите каждую группу роутов и пометьте её как продукт или обвязку — будьте беспощадны: страница настроек, которая лишь переключает URL вебхука, — это обвязка.
- Выпишите переменные окружения и отметьте, какие настраивают инфраструктуру (секреты аутентификации, ключи провайдеров), а какие — поведение продукта.
- Выпишите таблицы и отметьте, какие перейдут под управление кита (пользователи, сессии, воркспейсы, состояние биллинга), а какие остаются вашими.
- Запишите тот самый пользовательский сценарий, который приносит вам деньги, — этот поток будет приёмочным тестом для каждого этапа ниже.
Сопоставьте каждый стык с модулем кита
Инфраструктура кита — набор модулей с контрактами между ними; ваша задача — направить каждый кусок старой обвязки к модулю, который теперь за него отвечает. Большая часть сопоставления один к одному; там, где это не так, вы и принимаете настоящие решения.
Два выбора при сопоставлении заслуживают обдумывания, а не механического переноса. Если вы пришли с Prisma — кит стандартизирован на Drizzle: на время перехода Prisma можно оставить для продуктовых таблиц, но планируйте сойтись на одном варианте; две ORM в одной кодовой базе — налог на поддержку, который накапливается. Сравнение Prisma и Drizzle раскладывает, что именно меняется.
А если вы пришли от управляемого провайдера аутентификации — сдерживайте желание воспроизвести каждую его функцию до переключения. Managed auth незаметно упаковывает вещи, которыми ваш продукт может и не пользоваться: списки сессий, интерфейсы организаций, синхронизация вебхуков. Сопоставляйте то, что ваш продукт реально вызывает; сравнение решений аутентификации разбирает различия в возможностях, которые действительно важны.
| Ваша текущая обвязка | Модуль supastack | Что переезжает |
|---|---|---|
| Сессии NextAuth.js / Auth.js | Аутентификация (Better Auth) | Пользователи и аккаунты мигрируют; сессии перевыпускаются при переключении |
| Clerk или управляемый провайдер аутентификации | Аутентификация (Better Auth) | Пользователи мигрируют по email; сторонние сессии не переигрываются, а бросаются |
| Свои таблицы организаций/команд | Воркспейсы | Строки организаций становятся строками воркспейсов и членства |
| Самописные вебхуки Stripe | Биллинг | ID клиентов Stripe сохраняются; состояние подписки выводится заново |
| Задачи на CRON + setTimeout | Задачи (Trigger.dev) | Плановая работа переезжает за общий контракт JobRunner |
| Nodemailer / почта на коленке | Почта | Шаблоны переезжают; отправка идёт через один адаптер провайдера |
Внедряйте модули в порядке зависимостей
Порядок миграции — не вопрос вкуса, это граф зависимостей. Каждый этап ниже можно выпустить отдельно, и каждый даёт рабочую систему, которую вы проверяете до начала следующего. Соблазн — сделать всё за одни выходные и отлаживать пересечение; сопротивляйтесь: поэтапная миграция означает, что каждый встречный баг принадлежит ровно одному модулю, — отладочная суперсила, которую вы оцените, только когда её пропустите.
- Этап 1 — каркас: разверните кит, настройте env, положите продуктовые роуты в структуру проекта. Пока ничего не подключено: приложение запускается, и ваши страницы рендерятся.
- Этап 2 — аутентификация: переведите вход на Better Auth, мигрируйте таблицы пользователей и аккаунтов, проверьте вход и выход от начала до конца. Каждый следующий этап зависит от существования сессии.
- Этап 3 — тенантность: внедрите модель воркспейсов, заполните
workspaceIdв продуктовых таблицах задним числом, применяйте контекст в чтениях и записях. Именно здесь окупается статья о границах воркспейса. - Этап 4 — биллинг: направьте оформление заказа и вебхуки в биллинговый модуль, пересопоставьте существующие подписки, проверьте проверки прав доступа на реальных данных Stripe в тестовом режиме.
- Этап 5 — фоновая работа и почта: перенесите задачи на контракт JobRunner, а транзакционную почту — на адаптер email. Они последние, потому что зависят от уже существующих пользователей и воркспейсов.
Переносите данные честно
Миграция данных — то место, где миграции зарабатывают свою репутацию. Правила, которые держат её скучной: напишите скрипт, который можно запустить дважды, никогда не меняйте источник до переключения и проверяйте количества вместо того, чтобы верить скрипту.
Пользователи и аккаунты приземляются в схеме Better Auth — email служит ключом соединения, а для аккаунтов с паролем нужно решение: либо ваш текущий формат хеша умеет проверять Better Auth, либо при переключении вы выпускаете сценарий сброса пароля и относитесь к нему как к принудительной мере. Сторонние OAuth-аккаунты — простой случай: провайдер перевыпускает их при следующем входе.
В биллинге честный путь — перевывод, а не копирование строк: сохраните ID клиента Stripe, позвольте обработке вебхуков кита заново построить состояние прав доступа из истины провайдера и сравните то, что она вычисляет, с вашими текущими записями о подписках. Идемпотентный реестр вебхуков кита существует именно для того, чтобы переигранные исторические события ничего не провизионировали дважды.
// migration scripts are boring by design: upsert-shaped,
// re-runnable, and they verify counts instead of trusting themselves
for (const legacyUser of legacyUsers) {
await upsertAuthUser({
email: legacyUser.email.toLowerCase(),
name: legacyUser.displayName,
externalId: legacyUser.id, // preserved for one-shot joins
});
}
const migrated = await countAuthUsers();
if (migrated !== legacyUsers.length) {
throw new Error(`count mismatch: ${migrated} of ${legacyUsers.length}`);
}Что остаётся вашим
Стоит сказать прямо, потому что именно здесь люди ошибаются в обе стороны: ваши продуктовые роуты, схема, доменная логика и дизайн не меняются. Вы не перестраиваете приложение внутри кита — вы ставите его поверх кита.
Обратная ошибка — оставить старую обвязку живой «временно» рядом с новым модулем. Вторая проверка сессии, второй обработчик вебхуков, вторая таблица прав доступа — каждая «временная» параллельная система постоянна, пока кто-то её не удалит, и каждая — будущий инцидент. Замените, проверьте, удалите. Если этап пока не может удалить старый путь — он не закончен.
Чек-лист переключения
Переключение — это чек-лист, а не событие. Прогоните его на стейджинге хотя бы один раз с данными продакшн-формы до настоящей даты.
- Перезапустите скрипт данных на свежем продакшн-снимке; количества и выборочные проверки проходят.
- Войдите через каждый провайдер аутентификации, который поддерживает ваш продукт, — включая те, о существовании которых вы забыли.
- Купите собственный продукт в тестовом режиме Stripe и проследите вебхук до выдачи прав доступа.
- Отправьте каждое транзакционное письмо по разу; проверьте, что ссылки ведут на новый деплой.
- Проверьте платящий сценарий от начала до конца, затем переключите DNS/env и наблюдайте первую реальную сессию.
- Держите старый деплой запущенным неделю — откат это смена DNS, а не миграция.
Частые вопросы о переезде в стартовый кит
Одна и та же горстка вопросов всплывает каждый раз, когда кто-то садится за это дело.
- Сколько на самом деле занимает миграция? — Для одного веб-приложения со стандартной аутентификацией + Stripe: дни вечерами, а не недели. Мультитенантный B2B с кастомным биллингом добавляет времени на этапах три и четыре — растягивается именно работа с данными, никогда не перенос файлов.
- Мигрировать или начать с нуля и портировать функции? — Если ваш продуктовый код меньше инфраструктурного долга, старт на ките с нуля и перенос продуктовых роутов часто быстрее. Если схема и доменная логика — сложная часть, мигрируйте на месте.
- Можно ли оставить существующую ORM / библиотеку аутентификации на время перехода? — Ненадолго, да — по одной границе модулей за раз. Навсегда, нет: два экземпляра чего угодно в инфраструктурном слое — накапливающийся налог.
- А как насчёт моих вебхуков Stripe, которые уже в пути? — Направьте существующий эндпоинт вебхуков на обработчик кита, как только этап четыре проверен; идемпотентный реестр делает переигранные или продублированные события безопасными, а не опасными.
- Обязательно ли внедрять все модули? — Нет. Мобильный клиент, десктоп, задачи и AI — это поверхности, которые можно внедрять или игнорировать: лицензия — фундамент, а не обязанность выпускать приложение на каждую платформу.