
Перестань думать «Зарегистрирование пользователя». Начните думать «Новый арендатор»
9 июля 2025 г.В любом мультитенантском продукте SaaS в тот момент, когда новый пользователь подписывает больше, чем просто добавление строки в таблицу «пользователей». Этот пользователь представляет новую команду, новую границу данных и новую структуру разрешений.
Мы приняли сознательное решение рано:рассматривать создание учетной записи как корень архитектурыПолем
Вместо того, чтобы прицикливаться к организациям после создания пользователей или хранения данных организации как другое поле для пользователя, мы сделалисчетосновной объект. Пользователи принадлежат к учетным записям. Данные относятся к учетным записям. Разрешения происходят из счетов.
Это кадрирование повлияло на каждый кусок нашего бэкэнда от дизайна схемы до логики выставления счетов до RBAC.
Вот как мы его построили.
Переосмысление регистрации: учетная запись перед пользователем
Большая часть логики на адаптировании SaaS выглядит так:
POST /signup → Create user → Later: create org / assign team
Но в мультитенантском SaaS этот порядок приводит к тонким проблемам ... Данные без дома, разрешения в подвешенном состоянии и хрупкие предположения во всем вашем приложении.
Итак, мы начали с:
POST /signup → Create account → Create user inside account → Done
Это изменение может показаться небольшим, но оно сформировало все в нашей архитектуре в будущем.
Код выглядит примерно так:
export async function registerAccount(payload: RegistrationDTO) {
return withTransaction(async (session) => {
const account = await AccountModel.create([{
name: payload.company,
active: true
}], { session });
const user = await UserModel.create([{
accountId: account[0]._id,
email: payload.email,
role: 'admin'
}], { session });
return { account: account[0], user: user[0] };
});
}
Связывая пользователя непосредственно с учетной записью с первого дня и завернув его в транзакцию, мы избежали длинного списка краевых случаев, которые в противном случае быстро ползут.
Почему это важно: данные принадлежат арендаторам
Вот что мы узнали трудным путем:Данные не являются глобальными, это общежитие арендатора.
Каждый запрос, проверка разрешений, ворота функции и управление пользовательским интерфейсом в вашем приложении будут вести себя по -разному в зависимости отКто пользовательиКакую учетную запись они принадлежат.
Итак, мы сделали одно правило:
Все важное носит
accountId
Полем
Это включает в себя:
- Пользователи
- Битвы
- Разрешения
- Подписки
- Журналы аудита
- Функции переключаются
Держите транзакции плотными
При создании потока регистрации учетной записи одним из наших ключевых принципов былотделение проверки от настойчивостии быть намеренным огдеМы использовали транзакции базы данных.
Вот как это выглядит на практике:
await ensureEmailFree(payload.email);
await ensureCompanyFree(payload.company);
return withTransaction(async (session) => {
// Only the critical writes happen here
const account = await Account.create([{ name: payload.company }], { session });
const user = await User.create([{ email: payload.email, accountId: account[0]._id }], { session });
return { account: account[0], user: user[0] };
});
Почему? Таким образом, транзакция остается маленькой и быстрой, легче масштабировать, меньше проблем с блокировкой и легче восстановить от сбоев.
Bootstrap Admins, автоматически
Первым пользователем в любой учетной записи становится его администратор. Нет специальной логики, нет ручного размещения флагов. Он встроен прямо в поток.
Этот первый пользователь может:
- Пригласить их команду
- Установить роли и разрешения
- Настройка биллинга
- Перевести права администратора, если они уйдут
Это дало нам чистую, сдача в сфере борьбы с выкройкой, которая хорошо масштабировалась даже в первые дни.
Роли, которые росли с учетом аккаунтов, которые растут с вами
Мы не хотели заполнять ролевую систему, но нам нужно было достаточно структуры, чтобы охватить 90% реальных вариантов использования SaaS.
const ROLE_HIERARCHY = {
admin: ['admin', 'manager', 'user'],
manager: ['manager', 'user'],
user: ['user']
};
function hasPermission(userRole, requiredRole) {
return ROLE_HIERARCHY[userRole]?.includes(requiredRole) || false;
}
Мы оценили роли для учетных записей и использовали простое наследство. Это не причудливое, но это работает, и впоследствии при необходимости проще переработать в более детальной модели политики.
Подписки с первого дня
Мы дали каждой учетной записи объект подписки на создании, даже во время испытания. Это означало, что флаги функции, ограничения использования и подсказки обновления были связаны непосредственно с учетной записью с самого начала.
const defaultSubscription = {
tier: 'trial',
status: 'active',
limits: {
maxUsers: 5,
maxBattlecards: 10,
aiBattlecardGeneration: true
}
};
Мы используем это в функциях промежуточного программного обеспечения для ворот:
if (!account.subscription.limits.aiBattlecardGeneration) {
return res.status(402).json({ error: 'Upgrade required' });
}
Пространства имен, уникальность и изоляция
Некоторые вещи глобально уникальны. Некоторые из них являются арендатором. Мы нарисовали линию так:
Поле | Объем | Почему |
---|---|---|
| Глобальный | Один пользователь = одно электронное письмо |
| Глобальный | Используется в URL |
Другие данные | Жилец | Обсеян |
Что мы поняли правильно (и что вы можете украсть)
Этот маленький сдвиг,Создание аккаунта в центре адаптации,Окупился немедленно и продолжает сэкономить нам время сегодня.
Краткосрочные победы:
- Чистая изоляция данных
- Доступ на основе ролей с первого дня
- Функция логики флага, которая на самом деле работает
- Полностью функциональный пробный поток
Долгосрочные выплаты:
- Запросы легко охватить и оптимизировать
- Биллинг и разрешения разъединены
- Никакой странной миграции, когда вы «добавляете команды позже»
- Более безопасные значения по умолчанию везде
Последние мысли: начните с правильной ментальной модели
Страница регистрации может выглядеть просто, но она начинает самую важную структуру в вашем SaaS: арендатор.
Так что постройте так, как будто это имеет значение.
Начните с учетной записи. Прикрепите все остальное к нему. Применять границы рано. Все остальное, роли, разрешения, выставление счетов, становятся на свои места более естественно.
Вместо того, чтобы думать о «первом пользователе», и вместо этого мыслить «Первый аккаунт» дал нам архитектуру, которой мы могли бы доверять… и опираться.
Оригинал