Создание мультитенантного SaaS: рабочие пространства, роли, биллинг и изоляция

Как спроектировать рабочие пространства, управление доступом на основе ролей, тарифные планы и изоляцию арендаторов для масштабируемого SaaS. Практическая архитектура от DigiForge.

DFКоманда DigiForgeJul 20, 20268 мин чтения
Абстрактные светящиеся взаимосвязанные блоки на темном фоне, символизирующие мультитенантную архитектуру SaaS.

Мультитенантная SaaS-архитектура — это стандарт для любой B2B-платформы, которая планирует расти за пределы горстки клиентов. Однако дьявол кроется в деталях: то, как вы моделируете рабочие пространства, назначаете роли, структурируете биллинг и изолируете тенантов, определяет, будет ли ваша платформа масштабироваться гладко или рухнет под грузом сложности. В DigiForge мы строили и перестраивали такие системы для десятков SaaS-продуктов. Вот что мы узнали.

Рабочие пространства: основная единица организации

Рабочее пространство — это логический контейнер, который группирует пользователей, данные и конфигурацию для одного клиента (или команды внутри клиента). Мы видели, как команды путают рабочие пространства с биллинговыми аккаунтами или даже с проектами — не делайте так. Оставьте рабочее пространство как фундаментальную область видимости тенанта, а остальные концепции надстраивайте поверх.

Ключевые проектные решения:

  • Иерархическая или плоская структура? Некоторым платформам нужны рабочие пространства внутри рабочих пространств (например, предприятие с несколькими отделами). Мы рекомендуем двухуровневую иерархию: организация (биллинговая сущность) и рабочее пространство (командная единица). Избегайте более глубокой вложенности, если это не absolutely необходимо — она усложняет наследование ролей и доступ к данным.
  • Уникальные идентификаторы: Используйте человекочитаемый слаг (например, acme или acme-marketing) для рабочего пространства в URL, но всегда полагайтесь на UUID внутри системы. Слаги могут меняться; UUID — нет.
  • Мягкое удаление с льготным периодом: Удаление рабочего пространства — это радикальное действие. Внедрите 30-дневное мягкое удаление, чтобы пользователи могли восстановить данные. Немедленно прекратите биллинг, но сохраняйте данные до истечения льготного периода.

Когда новый пользователь регистрируется, продумайте процесс онбординга. Стоит ли ему сначала создать рабочее пространство или дать возможность исследовать песочницу? Мы предпочитаем направленный процесс, в котором пользователь создает организацию, затем свое первое рабочее пространство и сразу же получает предложение пригласить коллег. Это снижает трения и задает ожидание, что платформа предназначена для совместной работы.

Распространенная ошибка — привязывать тарифные планы напрямую к рабочим пространствам. Вместо этого привяжите биллинг к организации, которая содержит одно или несколько рабочих пространств. Это позволяет предприятиям получать единый счет, предоставляя каждой команде собственное рабочее пространство.

Роли и разрешения: детализированные, но не чрезмерно

Управление доступом на основе ролей (RBAC) является отраслевым стандартом, но важна степень детализации. В DigiForge мы обычно начинаем с трех встроенных ролей — Администратор, Участник, Наблюдатель — и допускаем создание пользовательских ролей для продвинутых тарифов. Администратор имеет полный контроль над рабочим пространством, Участники могут создавать и редактировать большинство ресурсов, а Наблюдатели могут только читать.

Сложности возникают с областью действия разрешений. По умолчанию разрешения должны быть ограничены рабочим пространством, но могут потребоваться разрешения на уровне организации (например, управление биллингом) или даже доступ на чтение между рабочими пространствами для консолидированной отчетности. Моделируйте разрешения как набор пар действие:ресурс и назначайте их ролям. Храните назначения в таблице связей: (workspace_id, user_id, role_id).

Совет профи: Не проверяйте права доступа только на уровне приложения. Переносите как можно больше логики разрешений в базу данных, используя безопасность на уровне строк (RLS) или механизм политик, например OPA. Это снижает вероятность того, что ошибка в веб-слое откроет доступ к чужим данным.

Один из паттернов, который хорошо себя зарекомендовал, — кэшировать роль пользователя в токене сессии (JWT), а не запрашивать базу данных при каждом запросе. Но будьте осторожны: если вы кэшируете роли в JWT, необходим механизм для аннулирования токенов при изменении роли (например, короткий срок действия токена или блок-лист).

Также подумайте о наследовании ролей: должен ли администратор организации автоматически становиться администратором во всех рабочих пространствах? Наше эмпирическое правило: роли организации задают верхний предел, но роли рабочего пространства могут быть более ограничительными. Например, администратор организации может получить доступ к любому рабочему пространству, но администратор рабочего пространства не может получить доступ к биллингу.

Биллинг и модели ценообразования: метаданные, а не бизнес-логика

Биллинг — это то, где мультитенантность становится реальной. Ваша модель ценообразования (за рабочее место, за рабочее пространство, на основе использования или по уровням) должна быть отражена в вашей модели данных, но система биллинга должна быть отделена от основного приложения. Используйте стороннего провайдера биллинга (Stripe, Recurly, Chargebee) и храните в базе данных только идентификатор подписки и идентификатор плана.

Мы рекомендуем следующий подход к базе данных:

  1. Таблица plans, определяющая слаг тарифа, цену и флаги функций (например, max_users, storage_gb, api_rate_limit).
  2. Таблица organizations с полями current_plan_id и billing_provider_subscription_id. Свяжите организации с рабочими пространствами через соединительную таблицу.
  3. Таблица features (или простая колонка JSON) для хранения переопределений. Например, если клиент договорился о специальной цене, переопределите цену тарифа на уровне организации.

Самая сложная часть — ограничение доступа на основе тарифа. У вас есть два варианта: применять ограничения в приложении (проверять max_users перед приглашением) или через подсчет строк в базе данных и триггеры. Мы предпочитаем контроль на уровне приложения, так как это дает более понятные сообщения об ошибках для пользователя, но всегда добавляем ночную задачу сверки, которая помечает организации, превышающие лимиты.

Обновления и понижения тарифа требуют осторожного подхода. При повышении тарифа немедленно предоставляйте доступ к новым функциям, но пропорционально рассчитывайте оплату через вашего провайдера. При понижении нужно решить: заблокировать доступ к функциям, превышающим новый тариф, или предоставить льготный период? Мы рекомендуем льготный период до конца текущего расчетного цикла, после чего применяются ограничения.

Один урок из окопов: никогда не допускайте, чтобы сбои в оплате приводили к потере данных. Если платеж не прошел, деградируйте функциональность корректно (например, ограничьте операции записи), но не удаляйте данные. Ваш клиент заплатит — в конце концов.

Изоляция арендаторов: общая база данных против выделенной

Изоляция — самое важное архитектурное решение. Стандартный компромисс — между общей базой данных (одна база для всех арендаторов, с колонкой tenant_id в каждой таблице) и базой данных на арендатора (каждое рабочее пространство получает собственную базу). Мы пробовали оба подхода и остановились на гибридном для большинства проектов.

  • Общая с строгим RLS: Хорошо подходит для небольших и средних арендаторов (до 10 тыс. пользователей каждый). Безопасность на уровне строк встроена в Postgres, и мы используем переменную сессии (app.tenant_id) для фильтрации каждого запроса. Это самый простой вариант в эксплуатации и обновлении.
  • База данных на арендатора: Необходима, когда арендаторы требуют строгого соответствия стандартам (HIPAA, SOC 2, GDPR, местонахождение данных) или когда приложение интенсивно по вводу-выводу на арендатора. Операционные накладные расходы реальны — миграции схемы нужно применять к сотням баз данных, но такие инструменты, как Flyway и автоматизированный CI, делают это управляемым.
  • Схема на арендатора: Промежуточный вариант с использованием отдельных схем в одной базе данных. Он обеспечивает большую изоляцию, чем общая таблица, но меньшие операционные накладные расходы, чем полноценные базы данных. Мы используем это для наших тарифов нижнего уровня и переводим клиентов на базу данных на арендатора, если им это нужно.

Независимо от стратегии изоляции, никогда не разрешайте прямой доступ к базе данных со стороны клиента. Всегда маршрутизируйте через API-уровень, который проверяет идентификатор арендатора. И ради вашей дежурной команды: никогда, никогда не используйте tenant_id в URL без проверки, что аутентифицированный пользователь принадлежит этому арендатору.

Миграция данных между уровнями изоляции — реальность. Например, когда арендатор перерастает общую базу данных, может потребоваться перенести его на выделенную базу. Планируйте это заранее: напишите скрипт миграции, который экспортирует и импортирует данные, и протестируйте его на данных, близких к реальным. Он должен работать без простоев, используя подход blue-green.

Автоматизированное развертывание: пусть машина делает это

Ручное создание новых арендаторов может работать для первых десяти клиентов, но не масштабируется. Как подчеркивает недавняя патентная заявка ContractorHUB, реализация без участия человека является ключевым отличием мультитенантного SaaS [1]. Мы создали рабочие процессы развертывания, которые автоматизируют всё: от создания базы данных (или клонирования схемы) до заполнения начальными данными и отправки приветственных писем.

Типичный конвейер автоматизированного развертывания:

  1. Пользователь регистрируется, создает организацию и выбирает тариф.
  2. Вебхук от вашего платежного провайдера запускает задачу развертывания (например, бессерверную функцию или задачу Kubernetes).
  3. Задача создает уровень изоляции арендатора (схему или базу данных), выполняет начальные миграции и заполняет роли и настройки по умолчанию.
  4. Вторая задача отправляет письмо с инструкциями по входу и следующими шагами.
  5. Пользователь перенаправляется в новое рабочее пространство — полностью функциональное — в течение нескольких секунд.

Идемпотентность здесь обязательна. Если задача развертывания завершается сбоем на полпути, повторный запуск должен быть безопасным. Мы оборачиваем весь процесс в конечный автомат с колонкой provisioning_status в строке организации: pending → creating → active → failed. Состояния сбоя отправляются в очередь недоставленных сообщений для вмешательства человека.

Не забудьте подготовить вспомогательную инфраструктуру: DNS-записи для пользовательских доменов, прогреватели CDN-кэша, лимиты скорости API для каждого арендатора и оповещения мониторинга. Автоматизируйте всё, что можно запрограммировать, потому что ручные шаги будут забыты под давлением.

Заключение: Мультиарендное мышление

Мультиарендность — это не то, что можно добавить после запуска. Она должна определять вашу модель данных, контроль доступа, интеграцию биллинга и стратегию развертывания с самого первого дня. Хорошая новость: если вы правильно реализуете эти четыре столпа — рабочие пространства, роли, биллинг и изоляцию, — остальная часть вашего SaaS станет значительно проще в разработке и поддержке.

Каждый SaaS уникален, но описанные выше паттерны хорошо зарекомендовали себя в разных отраслях. Начинаете ли вы свой MVP или масштабируетесь до тысяч арендаторов, мы призываем вас глубоко обдумать эти решения. А если вам нужен свежий взгляд на вашу архитектуру, мы всегда рады провести ревью.

#мультитенантность#saas#рабочие-пространства#роли#биллинг#изоляция#провижининг
DF

Команда DigiForge

Инженерная команда DigiForge — создаем современные websites, modules и автоматизацию, а также пишем о мастерстве выпуска быстрых и надежных веб-продуктов.

Давайте обсудим

Есть проект
на примете?

Расскажите нам, что вы создаете, — мы разработаем четкий план и подберем правильный подход к вашему продукту.

Начать проект