Разработка SaaS-платформ
SaaS — это не просто «сайт с авторизацией и подпиской». Это набор инфраструктурных решений, каждое из которых имеет нетривиальную реализацию: multi-tenancy, биллинг, onboarding, feature flags, rate limiting, audit logs. Ошибки в архитектуре на старте становятся очень дорогими при масштабировании.
Multi-tenancy: shared schema vs separate databases
Это первое архитектурное решение, которое нельзя легко поменять после запуска — и именно поэтому его нужно принять осознанно.
Shared schema (tenant_id на каждой таблице) — наиболее распространённый подход. Все арендаторы живут в одной базе данных, каждая запись помечена tenant_id. Проще в обслуживании, миграции применяются один раз, меньше операционной сложности.
Проблема: утечка данных между тенантами — это катастрофа. Один забытый WHERE tenant_id = ? в запросе — и пользователь видит чужие данные. В Laravel это решается через Global Scope на всех моделях:
protected static function booted(): void
{
static::addGlobalScope('tenant', function (Builder $builder) {
$builder->where('tenant_id', TenantContext::current()->id);
});
}
Плюс — обязательные тесты, которые проверяют: запрос от тенанта A не может вернуть данные тенанта B ни при каких условиях.
Separate databases — каждый тенант получает свою схему или базу данных. Полная изоляция данных, проще соответствие требованиям GDPR и ISO 27001 (данные клиента физически отделены). Минус: миграции нужно применять к каждому тенанту по очереди, при 1000+ тенантах это операция на часы.
Гибридный подход — shared database для большинства тенантов, dedicated database для enterprise-клиентов, которые платят за изоляцию. Это то, что делают большинство зрелых SaaS-продуктов.
Наш стандартный выбор для нового SaaS — shared schema с жёсткой tenant isolation через глобальные скоупы и row-level security в PostgreSQL. RLS добавляет второй слой защиты на уровне базы данных, даже если приложение допустит ошибку:
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.tenant_id')::uuid);
Биллинг: подписки, trials, upgrades
Биллинг — один из самых недооцениваемых по сложности блоков. Типичные ситуации, которые нужно обрабатывать:
- Upgrade в середине расчётного периода (prorata)
- Downgrade с немедленным или отложенным вступлением в силу
- Trial истёк, пользователь не добавил карту — что происходит с данными?
- Платёж провалился — grace period, dunning emails, блокировка доступа
- Возврат средств при отмене подписки
Stripe Billing закрывает большинство этих сценариев из коробки — это то, что мы используем по умолчанию. Webhook-события: customer.subscription.updated, invoice.payment_failed, invoice.payment_succeeded, customer.subscription.deleted. Каждый обрабатывается идемпотентно.
Idempotency key при создании платежа — обязателен. Без него retry logic на стороне клиента может привести к двойному списанию.
Для рынков СНГ — интеграция с ЮКасса или Tinkoff для подписок через recurring payments. Менее удобный API чем Stripe, но покрывает требования локального законодательства.
Onboarding: от регистрации до «aha moment»
Технически onboarding — это wizard с персистентным состоянием, который нельзя случайно пропустить и нельзя пройти дважды.
Типичная реализация: таблица onboarding_steps со статусом каждого шага для каждого пользователя. Middleware проверяет, завершён ли onboarding, и редиректит на незавершённый шаг. После завершения — флаг в user settings, middleware перестаёт срабатывать.
Важный нюанс: onboarding должен показывать прогресс реального продукта, не абстрактные шаги. «Создайте первый проект» вместо «Завершите настройку аккаунта (шаг 3 из 7)».
Email-последовательность onboarding — отдельный модуль. Drip-кампания через Mailgun/SendGrid с условной логикой: если пользователь сделал ключевое действие — следующее письмо не отправляется или меняется. Инструменты: Customer.io, Loops, или собственная очередь задач с отложенными jobs.
Feature flags и управление доступом
SaaS с несколькими тарифными планами требует гранулярного управления фичами: Free план видит А, Pro план видит B и C, Enterprise видит всё плюс D.
Не нужно делать это через if ($user->plan === 'pro') разбросанными по всему коду. Это становится неподдерживаемым быстро.
Правильный подход — feature flag система. Для Laravel: Gate + Policy с проверкой через план подписки, или отдельная таблица features с отношением к планам. Для фронтенда — контекст с флагами, загружаемый при инициализации приложения.
Open source решение: Growthbook или Unleash — позволяют управлять флагами через UI, делать A/B тесты, постепенный rollout.
Rate limiting и защита API
SaaS-платформа с публичным API обязана иметь rate limiting. Без него один агрессивный клиент может положить всех остальных.
В Laravel — RateLimiter фасад или throttle middleware. Для более сложных сценариев (разные лимиты по тарифным планам, лимиты по методам API) — Redis с sliding window counter:
Лимиты по плану: Free — 100 requests/hour, Pro — 1 000/hour, Enterprise — 10 000/hour. Заголовки X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset в каждом ответе — это стандарт, клиенты на него рассчитывают.
Мониторинг и audit logs
Audit log — обязательный компонент для SaaS, где несколько пользователей работают с общими данными. «Кто удалил этот проект?», «Когда изменились настройки биллинга?» — без лога ответить невозможно.
Таблица audit_logs: user_id, tenant_id, action, subject_type, subject_id, old_values, new_values, ip_address, created_at. Индексы по (tenant_id, created_at) и (subject_type, subject_id).
В Laravel — Observer'ы на ключевых моделях или пакет owen-it/laravel-auditing.
Мониторинг приложения: Sentry для exception tracking, Grafana + Prometheus или Datadog для метрик, Loki для логов. Алерты на: error rate > X%, response time p95 > 2s, failed payments spike.
Архитектура для масштабирования
MVP можно запустить на monolith — это нормально. Преждевременная микросервисная архитектура для SaaS на ранней стадии — это оверинжиниринг, который замедляет разработку без реальной пользы.
Monolith → Modular monolith → Microservices — правильная траектория при росте нагрузки.
Горизонтальное масштабирование монолита на Laravel: Laravel Octane (Swoole/RoadRunner) убирает overhead bootstrap на каждый запрос, horizontal scaling через несколько инстансов за Nginx/HAProxy, Redis для кешей и очередей, PostgreSQL с read replicas для чтения.
Ориентиры по срокам
| Этап | Срок |
|---|---|
| MVP (core features + auth + billing) | 12–16 недель |
| Полноценный продукт с admin panel | 20–28 недель |
| Enterprise SaaS с multi-tenancy + audit | 28–40 недель |
Стоимость зависит от количества ролей, сложности биллинговой логики, требований к интеграциям и уровня нагрузки. Рассчитывается после детального discovery.







