Все статьи

Как спроектировать SaaS-приложение с нуля: Multi-Tenancy, подписки и архитектура

Полное руководство для Full Stack разработчика: React, FastAPI, PostgreSQL, Stripe Billing, RBAC, Multi-Tenant Architecture, безопасность и масштабирование SaaS в Production

27 минут чтения

Представим, что вы хотите создать собственный SaaS-продукт. Например, систему управления проектами для небольших компаний. В приложении пользователи смогут:

  • регистрироваться и создавать организации;
  • приглашать сотрудников;
  • создавать проекты и задачи;
  • назначать роли и права доступа;
  • подключать платные тарифы;
  • оплачивать подписки;
  • использовать API;
  • хранить документы;
  • получать уведомления.

На первый взгляд такой продукт похож на обычное веб-приложение. Frontend на React. Backend на FastAPI. База данных PostgreSQL. Авторизация через JWT или Session Cookies. Но SaaS отличается от обычного приложения несколькими важными особенностями. Во-первых, одной системой пользуются независимые компании. Во-вторых, данные каждой компании должны быть изолированы.

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

В этой статье разберём, как создать SaaS приложение с нуля, выбрать Multi-Tenant Architecture, спроектировать PostgreSQL, реализовать RBAC, интегрировать Stripe Billing, организовать подписки, Feature Flags, квоты, безопасность и Production-инфраструктуру.


1. Что такое SaaS

SaaS Software as a Service — модель предоставления программного обеспечения через интернет. Пользователь обычно не устанавливает серверную часть продукта самостоятельно. Он открывает веб-приложение и получает доступ к функциональности через браузер или API. Примеры SaaS-продуктов:

  • CRM;
  • Project Management;
  • Email Marketing;
  • Analytics;
  • Help Desk;
  • Accounting;
  • HR Management;
  • Team Collaboration.

Часто SaaS использует подписочную модель оплаты, но подписка не является обязательным техническим признаком SaaS.

2. Чем SaaS отличается от обычного веб-приложения

SaaS vs Web Application: Главная архитектурная особенность B2B SaaS — необходимость безопасно обслуживать множество независимых клиентов.

3. Что такое Tenant

Tenant в SaaS — логически изолированный клиент системы. В B2B-приложении Tenant часто соответствует организации. Например: SaaS Platform | +-- Organization A | | | +-- Users | +-- Projects | +-- Documents | +-- Organization B | +-- Users +-- Projects +-- Documents Организация A не должна иметь доступа к данным организации B. Даже если обе организации используют одну базу данных.

4. Что такое Multi-Tenancy

Multi-Tenancy SaaS — архитектура, в которой одно программное решение обслуживает нескольких логически независимых клиентов. Например: React Frontend | v FastAPI Backend | v PostgreSQL | +---------+---------+ | | v v Tenant A Tenant B Клиенты могут использовать общие серверы, приложения и инфраструктуру. При этом их данные и полномочия должны оставаться изолированными.


5. Какие модели Multi-Tenancy существуют

Multi-Tenant Database Architecture обычно включает три основных подхода. Все организации используют общие таблицы. PostgreSQL | +-- organizations +-- users +-- projects +-- tasks Данные разделяются через organization_id. Организации используют одну базу данных, но разные схемы. PostgreSQL | +-- tenant_a.projects +-- tenant_b.projects Каждая организация получает отдельную базу данных. Tenant A -> Database A

Tenant B -> Database B Каждая модель имеет преимущества и ограничения.

6. Shared Database vs Database per Tenant

Shared Database vs Database per Tenant: Для небольшого SaaS часто разумно начинать с Shared Database. Но выбор зависит от требований к безопасности, производительности, восстановлению данных и регулированию.

7. Когда выбирать Separate Schema

Schema per Tenant PostgreSQL может быть полезна, если требуется более выраженное разделение данных без отдельной базы для каждой организации. Однако возникают сложности:

  • миграции для множества схем;
  • управление соединениями;
  • динамический search_path;
  • мониторинг;
  • обслуживание;
  • ограничения ORM.

Поэтому Separate Schema не всегда является оптимальным компромиссом.


8. Как спроектировать базу данных SaaS

PostgreSQL SaaS Database Design начинается с определения основных сущностей. Например: users organizations organization_members projects tasks subscriptions plans usage_events Важно сразу разделить глобального пользователя и его членство в конкретной организации. Один пользователь может работать с несколькими компаниями.

9. Таблица Users

Users Table SaaS: CREATE TABLE users ( id UUID PRIMARY KEY, email TEXT NOT NULL UNIQUE, password_hash TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); password_hash может быть NULL, если пользователь зарегистрирован через внешний Identity Provider. При этом схема аутентификации должна отдельно учитывать связанные внешние идентификаторы.

10. Таблица Organizations

Organizations Table SaaS: CREATE TABLE organizations ( id UUID PRIMARY KEY, name TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); Организация — отдельная бизнес-сущность. Её не следует автоматически отождествлять с одним пользователем.

11. Таблица Organization Members

Organization Membership RBAC: CREATE TABLE organization_members ( organization_id UUID NOT NULL REFERENCES organizations(id), user_id UUID NOT NULL REFERENCES users(id), role TEXT NOT NULL CHECK (role IN ('owner', 'admin', 'member', 'viewer')), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (organization_id, user_id) ); Теперь пользователь может иметь разные роли в разных организациях. Например: User 42 |

+-- Organization A: owner | +-- Organization B: viewer Это фундаментальная модель для B2B SaaS.

12. Как хранить проекты

Tenant ID PostgreSQL должен присутствовать в таблицах, принадлежащих конкретной организации. CREATE TABLE projects ( id UUID PRIMARY KEY, organization_id UUID NOT NULL REFERENCES organizations(id), name TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); Для эффективных запросов полезен индекс: CREATE INDEX idx_projects_org ON projects (organization_id);

Для операций поиска конкретного проекта в рамках организации можно использовать составные индексы и ограничения в зависимости от модели запросов.


13. Почему нельзя доверять Organization ID от клиента

Tenant Isolation Security — одна из самых важных задач SaaS. Предположим, клиент отправляет: { "organization_id": "org-b", "name": "Secret Project" } Если пользователь состоит только в Organization A, Backend не должен создавать проект в Organization B. Нельзя считать переданный organization_id доверенным. Сначала нужно проверить, что пользователь действительно имеет необходимые полномочия в указанной организации.

14. Как реализовать Tenant Context

Tenant Context FastAPI — проверенный контекст текущей организации. Он может определяться по:

  • URL;
  • поддомену;
  • выбранной организации в сессии;
  • явно переданному заголовку.

Но независимо от способа выбора Tenant Backend обязан проверить членство и права пользователя. Например: from dataclasses import dataclass @dataclass class TenantContext: organization_id: str user_id: str role: str Этот объект должен создаваться только после проверки аутентификации и членства.

15. Как реализовать Tenant Dependency в FastAPI

FastAPI Multi Tenant Dependency: from fastapi import Depends, HTTPException async def get_tenant_context( organization_id: str, current_user=Depends(get_current_user), ): membership = await membership_repository.find( user_id=current_user.id, organization_id=organization_id, ) if membership is None: raise HTTPException( status_code=403, detail="Access denied", ) return TenantContext( organization_id=organization_id,

user_id=current_user.id, role=membership.role, ) Здесь get_current_user и membership_repository — компоненты приложения, которые необходимо реализовать отдельно. Главный принцип: Tenant Context создаётся только после проверки членства.

16. Как использовать Tenant Context в Endpoint

FastAPI Tenant Authorization: @app.get( "/organizations/{organization_id}/projects" ) async def list_projects( tenant: TenantContext = Depends( get_tenant_context ), ): return await project_repository.list_for_organization( organization_id=tenant.organization_id ) Запрос к базе данных должен содержать ограничение по организации. Это помогает предотвращать BOLA/IDOR.

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


17. Что такое PostgreSQL Row-Level Security

PostgreSQL RLS Multi Tenancy — дополнительный механизм изоляции данных на уровне базы. Например: ALTER TABLE projects ENABLE ROW LEVEL SECURITY; Затем можно определить политику: CREATE POLICY tenant_projects_policy ON projects USING ( organization_id = NULLIF( current_setting( 'app.current_organization', true ), '' )::uuid ) WITH CHECK ( organization_id = NULLIF( current_setting( 'app.current_organization', true ),

'' )::uuid ); Эта политика предполагает, что Backend корректно устанавливает доверенный Tenant Context.

18. Какие ошибки возможны с RLS

RLS PostgreSQL Security Pitfalls:

  • приложение подключается под ролью, обходящей RLS;
  • используется суперпользователь;
  • неправильно устанавливается Tenant Context;
  • состояние соединения сохраняется между запросами;
  • политики не покрывают INSERT и UPDATE;
  • тесты выполняются под привилегированной ролью;
  • отсутствуют проверки на уровне бизнес-логики.

Для Pool соединений полезно устанавливать Tenant Context в рамках транзакции через SET LOCAL или эквивалентный механизм. Также нужно учитывать, что владелец таблицы по умолчанию может обходить RLS, если не используется FORCE ROW LEVEL SECURITY.


19. Как спроектировать роли SaaS

RBAC SaaS Application часто включает роли: Но роль сама по себе не должна определять абсолютно все действия. Например, даже Admin одной организации не должен иметь доступ к другой.

20. Как реализовать проверку прав

Role Based Access Control FastAPI: def require_roles(*allowed_roles): async def dependency( tenant: TenantContext = Depends( get_tenant_context ), ): if tenant.role not in allowed_roles: raise HTTPException( status_code=403, detail="Insufficient permissions", ) return tenant return dependency Использование: @app.post( "/organizations/{organization_id}/projects" ) async def create_project(

payload: CreateProjectRequest, tenant: TenantContext = Depends( require_roles("owner", "admin", "member") ), ): return await project_service.create( organization_id=tenant.organization_id, payload=payload, ) Это упрощённый пример. В Production необходимо дополнительно проверять права на конкретные объекты и бизнес-ограничения.

21. Что такое Permission-Based Authorization

Permissions SaaS Architecture — подход, при котором проверяются конкретные разрешения. Например: project.read project.create project.update project.delete member.invite member.remove billing.read billing.manage Роли могут объединять Permissions. Такой подход гибче, чем проверки вида: if user.role == "admin": разбросанные по всему Backend.


22. Как реализовать приглашения пользователей

User Invitations SaaS позволяют администраторам приглашать сотрудников. Типичный процесс: Admin | v Invite User | v Create Invitation Token | v Send Email | v User Accepts Invitation | v Create Membership Invitation Token должен иметь ограниченный срок действия. Также необходимо учитывать:

  • повторное использование;
  • отзыв приглашения;
  • проверку адреса;
  • существующие Membership;
  • конкурентное принятие приглашений.

23. Как безопасно хранить Invitation Token

Invitation Token Security: Вместо хранения токена в открытом виде можно хранить его криптографический хеш. При принятии приглашения Backend вычисляет хеш полученного токена и сравнивает его с сохранённым значением. Для случайного высокоэнтропийного токена может использоваться SHA-256. При этом обычные пользовательские пароли требуют специализированных алгоритмов Password Hashing.


24. Как спроектировать тарифы SaaS

SaaS Pricing Plans Architecture может включать: Free Starter Pro Business Enterprise Каждый тариф определяет:

  • доступные функции;
  • количество пользователей;
  • количество проектов;
  • объём хранилища;
  • лимиты API;
  • доступность интеграций;
  • уровень поддержки.

Но тарифы не должны быть жёстко зашиты в десятки разных Endpoint.

25. Таблица Plans

SaaS Plans Database Schema: CREATE TABLE plans ( id TEXT PRIMARY KEY, name TEXT NOT NULL, is_active BOOLEAN NOT NULL DEFAULT TRUE ); Например: free starter pro business Дополнительно можно хранить версии тарифов и параметры доступных возможностей.

26. Как хранить ограничения тарифов

SaaS Plan Limits: CREATE TABLE plan_limits ( plan_id TEXT NOT NULL REFERENCES plans(id), feature_key TEXT NOT NULL, limit_value BIGINT, PRIMARY KEY (plan_id, feature_key) ); Пример: NULL можно использовать для обозначения отсутствия тарифного ограничения, если это явно предусмотрено контрактом данных. Но отсутствие тарифного ограничения не означает отсутствие технических защит от злоупотреблений.


27. Что такое Feature Flags

Feature Flags SaaS позволяют включать и отключать функциональность. Например: advanced_analytics custom_domain api_access priority_support Это полезно для:

  • управления тарифами;
  • постепенного запуска функций;
  • A/B-тестирования;
  • пилотных клиентов;
  • аварийного отключения проблемной функции.

Но Feature Flags не должны использоваться как единственный механизм авторизации.

28. Как проверять доступность функции

Feature Entitlements SaaS — права организации на определённую функциональность. Например: async def require_feature( organization_id: str, feature_key: str, ): enabled = await entitlement_service.is_enabled( organization_id=organization_id, feature_key=feature_key, ) if not enabled: raise HTTPException( status_code=403, detail="Feature unavailable", )

Entitlement Service должен учитывать актуальное состояние подписки и тарифные права.


29. Как реализовать лимиты SaaS

SaaS Usage Limits могут ограничивать:

  • количество пользователей;
  • количество проектов;
  • количество запросов;
  • объём хранилища;
  • число экспортов;
  • использование AI-функций.

Важно различать: Quota — общий доступный объём ресурса за период или в рамках тарифа. Rate Limit — интенсивность запросов за короткий промежуток времени. Например: Quota: 1000 AI Requests / month Rate Limit: 20 Requests / minute Оба ограничения могут действовать одновременно.

30. Как защитить квоты от Race Condition

SaaS Quota Race Condition возникает, когда несколько запросов одновременно проверяют лимит. Например: Current Projects: 9 Limit: 10 Request A -> sees 9 Request B -> sees 9 Both create project В результате появляется 11 проектов. Для защиты можно использовать:

  • транзакции;
  • блокировки;
  • атомарные счётчики;
  • уникальные ограничения;
  • корректные уровни изоляции.

Для критичных квот нельзя полагаться только на проверку в Python перед записью.


31. Как организовать подписки

SaaS Subscription Management — управление жизненным циклом платного доступа. Например: Trial | v Active | +--> Past Due | +--> Canceled | +--> Unpaid Конкретные статусы зависят от платёжного провайдера и модели продукта. Важно разделять:

  • состояние подписки у платёжного провайдера;
  • локальное состояние Billing;
  • фактические Entitlements организации.

Эти состояния связаны, но не всегда идентичны.

32. Что такое Stripe Billing

Stripe Billing SaaS — платформа для управления подписками и периодическими платежами. Через Stripe можно реализовать:

  • Products;
  • Prices;
  • Customers;
  • Subscriptions;
  • Checkout;
  • Customer Portal;
  • Invoices;
  • Webhooks.

Но конкретная доступность функций и платёжных методов зависит от региона, аккаунта и настроек Stripe.

33. Как связать Stripe Customer с организацией

Stripe Customer Organization Mapping: CREATE TABLE billing_customers ( organization_id UUID PRIMARY KEY REFERENCES organizations(id), stripe_customer_id TEXT NOT NULL UNIQUE ); Обычно Stripe Customer связывают с организацией, если именно организация является плательщиком. Но в некоторых продуктах плательщиком может быть отдельный пользователь. Эту модель нужно определить до интеграции Billing.


34. Как создать Stripe Checkout Session

Stripe Checkout Subscription FastAPI:

import os

import stripe

stripe.api_key = os.environ["STRIPE_SECRET_KEY"] def create_checkout_session( customer_id: str, price_id: str, ): session = stripe.checkout.Session.create( mode="subscription", customer=customer_id, line_items=[ { "price": price_id, "quantity": 1, } ], success_url=( "https://app.example.com/billing/success" "?session_id={CHECKOUT_SESSION_ID}" ), cancel_url=( "https://app.example.com/billing" ), ) return session.url

Это сокращённый пример. В реальном Endpoint необходимо:

  • проверить авторизацию пользователя;
  • убедиться, что он имеет право управлять Billing;
  • получить Customer из доверенной базы;
  • выбирать Price из серверного Allowlist;
  • использовать подходящие Idempotency Keys;
  • учитывать уже существующую подписку.

Нельзя позволять клиенту произвольно указывать Stripe Customer ID другой организации.

35. Почему нельзя активировать тариф по Success URL

Stripe Checkout Security требует учитывать, что пользователь может открыть Success URL самостоятельно. Например: /billing/success Сам факт открытия страницы не подтверждает успешную оплату. Поэтому нельзя писать: organization.plan = "pro" только потому, что Frontend открыл страницу успеха. Нужно проверять состояние оплаты и подписки через доверенные серверные данные Stripe.

Обычно для синхронизации используют Webhooks.


36. Что такое Stripe Webhook

Stripe Webhooks FastAPI — механизм получения событий от Stripe. Например: Stripe | v POST /webhooks/stripe | v Verify Signature | v Process Event | v Update Billing State Webhook может сообщать об изменении подписки, оплате счёта и других событиях. Но события могут приходить повторно. Также нельзя рассчитывать на строгий порядок доставки.

37. Как проверять подпись Stripe Webhook

Stripe Webhook Signature Verification:

import os

import stripe

from fastapi import FastAPI, Request, HTTPException app = FastAPI() @app.post("/webhooks/stripe") async def stripe_webhook(request: Request): payload = await request.body() signature = request.headers.get( "stripe-signature" ) try: event = stripe.Webhook.construct_event( payload, signature, os.environ["STRIPE_WEBHOOK_SECRET"], ) except (ValueError, stripe.error.SignatureVerificationError): raise HTTPException(

status_code=400, detail="Invalid webhook", ) await webhook_service.enqueue(event) return {"received": True} webhook_service — компонент, который необходимо реализовать отдельно. В Production важно ограничивать размер тела запроса и корректно обрабатывать ошибки постановки события в очередь.

38. Почему Webhook должен быть Idempotent

Stripe Webhook Idempotency — обязательный принцип надёжной обработки событий. Например, одно событие может прийти дважды. Если обработчик дважды начислит бонус или создаст два одинаковых Billing Record, возникнет ошибка. Поэтому полезно хранить идентификаторы обработанных событий: CREATE TABLE processed_webhook_events ( event_id TEXT PRIMARY KEY, processed_at TIMESTAMPTZ NOT NULL DEFAULT NOW() );

Но простая проверка существования записи перед обработкой недостаточна при конкурентных запросах. Нужны транзакции и атомарные ограничения.

39. Почему порядок Webhooks нельзя считать гарантированным

Stripe Webhook Event Ordering требует учитывать, что события могут прийти не в ожидаемой последовательности. Например: subscription.updated subscription.created Обработчик не должен слепо считать, что каждое новое полученное событие отражает более новое состояние подписки. Для важных изменений полезно получать актуальное состояние объекта через Stripe API и синхронизировать локальную модель.


40. Как хранить подписки в PostgreSQL

SaaS Subscriptions Database Schema: CREATE TABLE subscriptions ( id UUID PRIMARY KEY, organization_id UUID NOT NULL REFERENCES organizations(id), stripe_subscription_id TEXT UNIQUE, plan_id TEXT NOT NULL REFERENCES plans(id), status TEXT NOT NULL, current_period_end TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); Это базовая схема. Для полноценного Billing могут потребоваться дополнительные поля:

  • Customer;
  • Price;
  • Trial;
  • Cancellation;
  • Billing Interval;
  • Quantity;
  • Subscription Items;
  • версия синхронизированного состояния.

41. Что такое Subscription Lifecycle

Subscription Lifecycle SaaS включает: Create | v Trial | v Active | +--> Upgrade | +--> Downgrade | +--> Payment Failure | +--> Cancellation Важно заранее определить бизнес-поведение каждого перехода. Например: Что происходит при неудачном платеже? Сохраняется ли доступ во время Grace Period? Когда вступает в силу Downgrade? Что делать с данными, превышающими лимит нового тарифа?

Эти решения относятся не только к Billing, но и к продуктовой архитектуре.

42. Как реализовать Grace Period

SaaS Grace Period Billing — период, в течение которого клиент может сохранить часть функциональности после проблем с оплатой. Например: Payment Failed | v Grace Period | +--> Payment Recovered | +--> Access Restricted Grace Period должен быть явно определён бизнес-правилами. Не стоит автоматически удалять данные организации сразу после неудачной оплаты.


43. Как реализовать Upgrade и Downgrade

SaaS Upgrade Downgrade Subscription — изменение тарифа. При Upgrade могут измениться:

  • доступные функции;
  • лимиты;
  • стоимость;
  • период оплаты.

При Downgrade важно учитывать превышение текущих лимитов. Например: Current Plan: Pro Projects: 50 New Plan: Free Project Limit: 3 Возможные политики:

  • запретить создание новых проектов;
  • сохранить существующие проекты в режиме Read-Only;
  • предложить архивирование;
  • отложить изменение тарифа до конца периода.

Не следует автоматически удалять 47 проектов.

44. Как реализовать Proration

Stripe Proration SaaS — перерасчёт стоимости при изменении подписки. Например, пользователь переходит с Starter на Pro посреди оплаченного периода. Stripe может рассчитывать корректировки в зависимости от настроек. Но точное поведение зависит от Billing Configuration и параметров обновления подписки. Перед запуском необходимо тестировать Upgrade, Downgrade, Trial и отмену подписки в тестовой среде.


45. Как реализовать Customer Portal

Stripe Customer Portal позволяет пользователям управлять некоторыми Billing-операциями через интерфейс Stripe. Например:

  • обновлять платёжные данные;
  • просматривать счета;
  • управлять подпиской;
  • изменять тариф при соответствующей настройке.

Это уменьшает количество Billing-интерфейсов, которые нужно разрабатывать самостоятельно. Но Backend всё равно должен проверять, что пользователь имеет право открыть Portal для конкретной организации.


46. Как реализовать Usage-Based Billing

Usage Based Billing SaaS — модель оплаты на основе потребления. Например: 1000 API Requests included Additional Requests: billed separately Или: AI Tokens Storage GB Processed Documents Для такой модели необходим надёжный учёт использования.

47. Как хранить Usage Events

SaaS Usage Tracking: CREATE TABLE usage_events ( id UUID PRIMARY KEY, organization_id UUID NOT NULL REFERENCES organizations(id), feature_key TEXT NOT NULL, quantity BIGINT NOT NULL CHECK (quantity > 0), occurred_at TIMESTAMPTZ NOT NULL, idempotency_key TEXT NOT NULL, UNIQUE (organization_id, idempotency_key) ); Usage Events можно агрегировать для отчётов и Billing.

Важно избегать двойного учёта при повторной обработке событий.

48. Как организовать Metering

SaaS Metering Architecture: API Request | v Business Operation | v Usage Event | v Queue | v Usage Aggregator | v Billing System Для финансово значимых операций полезно использовать надёжную доставку событий, например Transactional Outbox. Это снижает риск ситуации, когда бизнес-операция выполнена, но Usage Event потерян.


49. Как организовать Frontend SaaS

React SaaS Frontend Architecture может включать: src/ app/ pages/ features/ entities/ shared/ Например: features/ auth/ organizations/ projects/ members/ billing/ settings/ Важно разделять:

  • Authentication State;
  • Active Organization;
  • Permissions;
  • Subscription Entitlements;
  • UI State.

Frontend может скрывать недоступные функции. Но Backend всё равно обязан проверять права и тарифные ограничения.

50. Как реализовать переключение организаций

Organization Switcher React позволяет пользователю выбирать текущую организацию. Например: Current Organization: Acme Inc. Switch to: Beta Studio После переключения нужно обновить:

  • Tenant Context;
  • Query Cache;
  • Permissions;
  • Subscription State;
  • маршруты;
  • активные WebSocket-подписки.

Особенно важно очищать или разделять кеш данных по Tenant.

51. Как избежать утечки данных через React Query

TanStack Query Multi Tenant требует включать Tenant ID в Query Keys. Плохой пример: useQuery({ queryKey: ["projects"], queryFn: fetchProjects, }); Если пользователь переключает организацию, кеш может содержать проекты предыдущей компании. Лучше: useQuery({ queryKey: [ "organizations", organizationId, "projects", ], queryFn: () => fetchProjects(organizationId), enabled: Boolean(organizationId), });

Это уменьшает риск отображения устаревших данных другого Tenant. Но не заменяет Backend Authorization.


52. Как организовать API SaaS

REST API SaaS Architecture может использовать маршруты: GET /api/organizations POST /api/organizations GET /api/organizations/{organization_id}/projects POST /api/organizations/{organization_id}/projects GET /api/organizations/{organization_id}/members POST /api/organizations/{organization_id}/invitations GET /api/organizations/{organization_id}/billing POST /api/organizations/{organization_id}/billing/checkout

Такой подход делает Tenant Context явным. Но возможны и другие схемы, например поддомены организаций. Главное — последовательная модель авторизации.

53. Как версионировать API

API Versioning SaaS: /api/v1/projects /api/v2/projects Версионирование особенно важно, если API используют внешние клиенты. Но не каждое изменение требует новой версии. Например, добавление необязательного поля часто можно сделать обратно совместимым. Удаление поля или изменение его семантики может потребовать отдельной стратегии миграции.


54. Как организовать фоновые задачи

Celery Redis SaaS Backend полезны для:

  • отправки Email;
  • генерации отчётов;
  • обработки файлов;
  • синхронизации Billing;
  • отправки уведомлений;
  • импорта данных;
  • очистки устаревших ресурсов.

Например: FastAPI | v Redis / RabbitMQ | v Celery Worker | v Background Task Важно передавать Tenant Context в задачу безопасным способом. Worker должен заново проверять необходимые данные и не доверять произвольному идентификатору организации без контекста операции.

55. Как избежать дублирования задач

Idempotent Background Jobs SaaS — важный принцип. Например, задача отправки счёта может выполниться повторно после сбоя. Поэтому критичные операции должны учитывать повторную доставку. Для этого используют:

  • Idempotency Keys;
  • уникальные ограничения;
  • транзакции;
  • явные статусы обработки;
  • повторные попытки с Backoff.

56. Как хранить файлы SaaS

S3 Object Storage SaaS подходит для документов, изображений и других файлов. Например: S3 Bucket | +-- organizations/ | +-- org-a/ | +-- org-b/ Но структура Object Keys сама по себе не гарантирует безопасность. Backend должен проверять права доступа перед выдачей Presigned URL. Также нужно учитывать:

  • размер файлов;
  • тип содержимого;
  • срок действия URL;
  • антивирусное сканирование;
  • квоты хранилища;
  • удаление;
  • жизненный цикл объектов.

57. Как учитывать Storage Quota

SaaS Storage Quota: Plan: Pro Storage Limit: 100 GB Current Usage: 72 GB При загрузке нового файла Backend должен проверять доступный объём. Но при параллельных Upload возможны Race Conditions. Поэтому для строгих квот полезно резервировать объём перед загрузкой и корректировать его после подтверждения размера объекта.


58. Как масштабировать SaaS

SaaS Scalability Architecture включает:

  • горизонтальное масштабирование Backend;
  • балансировку нагрузки;
  • Redis;
  • очереди задач;
  • оптимизацию PostgreSQL;
  • CDN;
  • Object Storage;
  • мониторинг;
  • ограничение потребления ресурсов отдельными Tenant.

Например: CDN | v Load Balancer | +---------+---------+ | | | v v v FastAPI 1 FastAPI 2 FastAPI 3 | | | +---------+---------+ | +------+------+ | | v v PostgreSQL Redis Но масштабирование должно опираться на реальные метрики.

59. Что такое Noisy Neighbor

Noisy Neighbor SaaS — ситуация, когда один Tenant потребляет слишком много общих ресурсов. Например: Tenant A: 100 requests / minute Tenant B: 100000 requests / minute Tenant B может ухудшить производительность для остальных клиентов. Для защиты используют:

  • Per-Tenant Rate Limiting;
  • квоты;
  • ограничения конкурентности;
  • отдельные очереди;
  • приоритизацию задач;
  • выделенные ресурсы для крупных клиентов.

60. Когда нужен Database Sharding

Database Sharding SaaS — разделение данных между несколькими базами или узлами. Например: Tenant A -> Database 1 Tenant B -> Database 2 Tenant C -> Database 3 Sharding может понадобиться при значительном росте нагрузки или объёма данных. Но он усложняет:

  • миграции;
  • транзакции;
  • отчётность;
  • маршрутизацию;
  • резервное копирование;
  • поддержку.

Поэтому начинать с Sharding без измеримых причин обычно не стоит.


61. Как мониторить SaaS

SaaS Observability включает технические и бизнес-метрики. Технические:

  • Request Rate;
  • Error Rate;
  • P95/P99 Latency;
  • CPU;
  • Memory;
  • Database Connections;
  • Queue Depth;
  • Cache Hit Rate.

Бизнес-метрики:

  • Active Organizations;
  • Active Users;
  • Trial Conversions;
  • Paid Subscriptions;
  • Churn;
  • MRR;
  • ARPU;
  • Usage per Tenant.

Технические метрики показывают состояние системы. Бизнес-метрики помогают оценивать развитие продукта.

62. Что такое MRR

Monthly Recurring Revenue SaaS — месячная регулярная выручка от подписок. Например: 100 customers × $20/month = $2000 MRR Но реальные расчёты могут учитывать:

  • скидки;
  • годовые подписки;
  • изменения тарифов;
  • валюты;
  • возвраты;
  • отмены.

Для финансовой отчётности необходимо определить единую методологию расчёта.

63. Что такое Churn

SaaS Churn Rate — показатель потери клиентов или регулярной выручки. Например: Customers at start: 100 Customers lost: 5 Customer Churn: 5% Это упрощённый пример. В SaaS также используют Revenue Churn и Net Revenue Retention. Эти показатели помогают понять, удерживает ли продукт клиентов.


64. Как обеспечить безопасность Multi-Tenant SaaS

Multi Tenant SaaS Security Checklist включает:

  • проверку Tenant Membership;
  • Object Level Authorization;
  • защиту от BOLA/IDOR;
  • безопасные роли;
  • RLS при необходимости;
  • изоляцию кеша;
  • проверку прав на файлы;
  • защиту WebSocket;
  • безопасный Billing;
  • проверку Webhook;
  • Audit Logs;
  • ограничения ресурсов.

Особенно важно тестировать сценарии между двумя организациями. Например: User A belongs to Organization A Document B belongs to Organization B User A must not read Document B Такие тесты должны быть частью CI/CD.

65. Как организовать Audit Log

SaaS Audit Logging помогает фиксировать важные действия. Например: User 42 invited User 99 User 42 changed billing plan User 99 deleted Project 123 Audit Log полезен для:

  • расследования инцидентов;
  • поддержки;
  • корпоративных клиентов;
  • анализа изменений;
  • выполнения требований безопасности.

При этом Audit Log должен быть защищён от несанкционированного изменения и не должен раскрывать секреты.


66. Как развернуть SaaS в Production

SaaS Deployment Architecture может включать: Internet | v DNS / CDN | v Nginx / Traefik | v FastAPI | +--> PostgreSQL | +--> Redis | +--> Celery | +--> S3 | +--> Stripe Для небольшого проекта достаточно начать с Docker Compose или управляемой платформы. По мере роста можно переходить к Kubernetes или другой оркестрации. Но Kubernetes не является обязательным условием успешного SaaS.

67. Как организовать CI/CD

CI/CD SaaS Application: Git Push | v Lint | v Unit Tests | v Integration Tests | v Security Checks | v Build Docker Image | v Deploy | v Health Checks Особенно важно тестировать миграции базы данных. Изменения схемы должны быть совместимы с процессом развёртывания. Для Zero Downtime Deployment часто применяют подход Expand and Contract.


68. Как организовать резервное копирование

SaaS Backup and Disaster Recovery должно учитывать:

  • PostgreSQL Backups;
  • Point-in-Time Recovery;
  • Object Storage;
  • секреты и конфигурацию;
  • восстановление после ошибок;
  • удаление данных;
  • требования клиентов.

Важно не только создавать резервные копии, но и регулярно проверять восстановление. Необходимо определить: RPO — допустимую потерю данных по времени. RTO — допустимое время восстановления. Эти значения зависят от SLA и бизнес-требований.

69. Как удалять данные Tenant

Tenant Data Deletion SaaS — сложная операция. Данные организации могут находиться в:

  • PostgreSQL;
  • Redis;
  • Object Storage;
  • поисковом индексе;
  • очередях;
  • логах;
  • резервных копиях;
  • внешних интеграциях.

Поэтому удаление Tenant требует отдельного процесса. Также необходимо учитывать юридические обязанности хранения финансовых документов и применимые требования защиты данных.


70. Как спроектировать SaaS MVP

SaaS MVP Architecture должна быть максимально простой, но безопасной. Для первого релиза часто достаточно: React + TypeScript | v FastAPI | v PostgreSQL Дополнительно: Redis Celery S3 Stripe Nginx Но не все компоненты обязательны с первого дня. Например, Redis и Celery можно добавить, когда появятся фоновые задачи или распределённые ограничения.

Главное — заранее правильно спроектировать Tenant Isolation и Billing State.

71. Что включить в первый релиз

SaaS MVP Features:

  1. Регистрация и вход.
  2. Создание организации.
  3. Приглашение участников.
  4. Роли и права.
  5. Основная бизнес-функция.
  6. Изоляция данных.
  7. Базовые тарифы.
  8. Подписки.
  9. Управление Billing.
  10. Логи ошибок.
  11. Резервное копирование.
  12. Автоматические тесты критичных сценариев.

Сложные микросервисы, Sharding и Enterprise-функции можно отложить до появления реальной потребности.


72. Типичные ошибки при разработке SaaS

Первая ошибка — хранить роль пользователя только в таблице Users. Вторая — не учитывать членство в нескольких организациях. Третья — доверять organization_id от клиента. Четвёртая — забывать Tenant Filter в SQL-запросах. Пятая — считать UUID полноценной защитой. Шестая — использовать общий React Query Cache без Tenant ID. Седьмая — активировать платный тариф после открытия Success URL.

Восьмая — не проверять подпись Stripe Webhook. Девятая — не учитывать повторную доставку Webhooks. Десятая — считать порядок событий гарантированным. Одиннадцатая — жёстко зашивать тарифы в Backend. Двенадцатая — не защищать квоты от конкурентных запросов. Тринадцатая — удалять данные при Downgrade без продуманной политики. Четырнадцатая — не ограничивать нагрузку отдельных Tenant.

Пятнадцатая — хранить секреты в React. Шестнадцатая — не тестировать межорганизационный доступ. Семнадцатая — не учитывать фоновые задачи при удалении организации. Восемнадцатая — не проверять восстановление Backups. Девятнадцатая — внедрять микросервисы слишком рано. Двадцатая — масштабировать инфраструктуру до появления реальных метрик.

Эти ошибки SaaS Architecture могут приводить к утечкам данных, проблемам Billing, нестабильности системы и росту операционных расходов.


73. Рекомендуемая архитектура SaaS для Full Stack разработчика

Full Stack SaaS Architecture React FastAPI PostgreSQL: Internet | v CDN / WAF | v Nginx / Traefik | +-----------+-----------+ | | v v React Frontend FastAPI Backend | +------------+------------+ | | | v v v PostgreSQL Redis Queue | | v v Tenant Data Celery | v Background Jobs External Integrations: | +-- Stripe Billing | +-- Email Provider | +-- S3 Object Storage | +-- Monitoring Основные принципы такой архитектуры:

Frontend отвечает за пользовательский интерфейс. FastAPI выполняет бизнес-логику и авторизацию. PostgreSQL хранит данные организаций. Redis поддерживает кеширование и некоторые распределённые механизмы. Очереди задач позволяют обрабатывать длительные операции. Stripe управляет платёжной инфраструктурой. Object Storage хранит файлы. Reverse Proxy управляет входящим трафиком.

Мониторинг помогает обнаруживать ошибки и проблемы производительности. Но важнее всего не количество технологий. Главное — корректные границы ответственности между компонентами.


74. Чек-лист запуска SaaS в Production

SaaS Production Checklist:

  • Выбрана Multi-Tenancy модель.
  • Определена сущность Tenant.
  • Пользователь может состоять в нескольких организациях.
  • Реализован Tenant Context.
  • Все Tenant-Owned таблицы имеют корректную модель изоляции.
  • Реализована Object Level Authorization.
  • Проверены сценарии BOLA/IDOR.
  • Пароли хранятся безопасно.
  • Сессии или JWT проверяются корректно.
  • Роли привязаны к организациям.
  • Billing Permissions отделены от обычных прав.
  • Invitation Tokens имеют срок действия.
  • Реализован отзыв доступа.
  • Stripe Customer связан с правильной организацией.
  • Price выбирается сервером.
  • Checkout доступен только уполномоченным пользователям.
  • Success URL не активирует подписку напрямую.
  • Stripe Webhook проверяется по подписи.
  • Webhook обработка Idempotent.
  • Учтены повторные события.
  • Учтён неправильный порядок событий.
  • Определена политика Payment Failure.
  • Определена политика Downgrade.
  • Настроен Customer Portal.
  • Лимиты тарифов определены централизованно.
  • Feature Entitlements проверяются на Backend.
  • Квоты защищены от Race Conditions.
  • Usage Events учитываются без дублирования.
  • Настроен Per-Tenant Rate Limiting.
  • Контролируется Noisy Neighbor.
  • HTTPS настроен.
  • Секреты хранятся безопасно.
  • Backend не опубликован без необходимости.
  • Настроены Health Checks.
  • Есть CI/CD.
  • Настроены Logs и Metrics.
  • PostgreSQL резервируется.
  • Проверено восстановление Backups.
  • Настроены Alerts.
  • Есть план реагирования на инциденты.
  • Определены тарифы.
  • Определены Trial и Grace Period.
  • Есть понятный Billing UI.
  • Пользователь может управлять организацией.
  • Есть процесс удаления данных.
  • Настроены продуктовые метрики.
  • Есть механизм поддержки клиентов.

FAQ

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

Shared Database обычно проще и дешевле на старте. Database per Tenant может быть полезна при повышенных требованиях к изоляции и управлению отдельными клиентами. Не обязательно, но RLS может стать полезным дополнительным уровнем защиты в Shared Database архитектуре. Лучше привязывать роли к Membership пользователя в организации, а не только к глобальной записи User.

Проверять Tenant Membership, Object Level Authorization и применять Tenant Filters ко всем операциям с данными. Да. Но JWT должен корректно проверяться, а права доступа должны оцениваться отдельно. Можно использовать Stripe Billing или другого платёжного провайдера. Через доверенные серверные данные платёжного провайдера и корректную обработку событий. Нет. Открытие Success URL само по себе не подтверждает оплату.

Серверное уведомление Stripe о событиях, связанных с платежами, подписками и другими объектами. Потому что одно событие может быть доставлено повторно. Право организации использовать конкретную функцию на основании тарифа, подписки или индивидуальных настроек. Quota ограничивает общий объём потребления, а Rate Limit — интенсивность запросов. Оплата, зависящая от фактического потребления ресурса.

Ситуация, когда один клиент потребляет слишком много общих ресурсов и ухудшает работу других. Нет. Для многих SaaS-продуктов модульный монолит является хорошей стартовой архитектурой. Нет. Kubernetes полезен при определённых требованиях к оркестрации, но не обязателен для запуска продукта. Monthly Recurring Revenue — месячная регулярная выручка от подписок.

Показатель потери клиентов или регулярной выручки за определённый период. React + TypeScript + FastAPI + PostgreSQL — один из практичных вариантов. Redis, Celery, S3 и Stripe можно добавлять по мере необходимости.


Заключение

Создание SaaS-приложения — это не просто разработка Frontend и Backend. Главная сложность заключается в правильном проектировании организаций, изоляции данных, прав доступа, тарифов, подписок и жизненного цикла клиентов. Multi-Tenancy позволяет обслуживать множество компаний в одной системе. Но требует строгой проверки Tenant Context и Object Level Authorization. RBAC помогает управлять ролями пользователей.

Но роли должны быть связаны с конкретной организацией. Stripe Billing упрощает работу с подписками. Но Backend должен корректно проверять Webhooks, учитывать повторные события и управлять состоянием подписки. Feature Entitlements позволяют централизованно контролировать функциональность тарифов. Usage Tracking помогает реализовать квоты и оплату по потреблению.

Redis, очереди задач и Object Storage позволяют постепенно масштабировать инфраструктуру. При этом сложность архитектуры должна соответствовать реальным требованиям продукта. Для первого SaaS обычно не нужны десятки микросервисов, Kubernetes и Sharding.

Гораздо важнее правильно спроектировать PostgreSQL, обеспечить изоляцию организаций, реализовать безопасный Billing и покрыть критичные бизнес-сценарии автоматическими тестами. Главный принцип SaaS-архитектуры: начинайте с простого модульного приложения, но с самого начала правильно проектируйте границы Tenant, полномочия пользователей и финансовые операции.

Именно эти решения сложнее всего исправлять после появления первых платящих клиентов.


Наверх