Decision Trees — как выбирать архитектуру
Практический навигатор по архитектурным решениям. Не истина в последней инстанции — а структура для размышлений. Каждый пункт — это не правило, а вопрос, который стоит себе задать.
#Содержание
- Как пользоваться этим аппендиксом
- A.1 Монолит vs Микросервисы — Decision Tree
- A.2 SQL vs NoSQL — Decision Guide
- A.3 REST vs GraphQL vs gRPC — Decision Matrix
- A.4 Сравнение NoSQL-систем — когда что
- A.5 SPA vs SSR vs SSG — Decision Framework
- A.6 Быстрый reference: типовые сценарии
#Как пользоваться этим аппендиксом
Это не глава для чтения подряд. Это шпаргалка для моментов, когда вы упираетесь в развилку и не знаете, куда идти.
Как устроен каждый раздел:
- Decision Tree (ASCII) — идёте по вопросам, получаете рекомендацию
- Decision Matrix (таблица) — сравниваете критерии
- Контекстные нюансы — когда рекомендация не работает
- Чек-лист для самопроверки — 3-5 вопросов после выбора
ВажноВажно: Эти деревья не говорят «делай X». Они говорят «если Y, то X — разумный выбор, но проверь Z». Архитектура — это про контекст, а не про rules of thumb.
#A.1 Монолит vs Микросервисы — Decision Tree
#Дерево решений
┌─────────────────────────────────────────────────────┐ │ ВЫ НАЧИНАЕТЕ НОВЫЙ ПРОЕКТ │ └─────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────┐ │ У вас уже есть продукт в продакшене? │ └─────────────────────────────────────────────────────┘ │ │ ДА НЕТ │ │ ▼ ▼ ┌──────────────┐ ┌─────────────────────┐ │ Продукт живёт │ │ СТАРТУЙТЕ │ │ на монолите? │ │ С МОНОЛИТА │ │ ДА ──────► ── │ │ (Серьёзно. │ │ ▼ │ │ Не надо мик- │ │ ┌───────────┐ │ │ росервисов │ │ │ Вам реально│ │ │ на старте) │ │ │ нужен │ │ └─────────────────────┘ │ │ микросер- │ │ │ │ вис? или │ │ │ │ хочется? │ │ │ └───────────┘ │ │ │ │ │ ДА │ │ │ │ │ ▼ │ │ ┌───────────┐ │ │ │ ИДИТЕ К │ │ │ │ ВОПРОСАМ │ │ │ │ НИЖЕ │ │ │ └───────────┘ │ └──────────────┘Если у вас нет продукта в продакшене — ответ всегда «монолит». Точка. Не читайте дальше, идите пишите код.
Если продукт уже живёт на монолите — честно ответьте на вопросы:
┌──────────────────────────────────────────────┐ │ В чём именно монолит мешает? │ │ (Выберите все подходящие варианты) │ └──────────────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Команда │ │ Не можем │ │ Одна ошибка │ │ наступает │ │ масштабиро- │ │ валит весь │ │ друг другу │ │ вать часть │ │ сервис │ │ на пятки │ │ системы │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Попробуйте │ │ Попробуйте │ │ Внедрите │ │ модульный │ │ read replica │ │ Circuit │ │ монолит: │ │ + CQRS: │ │ Breaker + │ │ разделите │ │ это дешевле │ │ Bulkhead: │ │ код на │ │ микросерви- │ │ это решит │ │ bounded │ │ сов и может │ │ 80% проблем │ │ contexts │ │ хватить │ │ изоляции │ │ в одном │ │ │ │ без полной │ │ процессе │ │ │ │ декомпозиции │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ └──────────────────┼──────────────────┘ │ ▼ ┌─────────────────────┐ │ Модульный монолит │ │ НЕ РЕШИЛ проблему? │ └─────────────────────┘ │ ДА │ ▼ ┌─────────────────────┐ │ ИДЁМ К ПОЛНОМУ │ │ DECISION TREE │ └─────────────────────┘#Полное дерево: когда переходить к микросервисам
START HERE (если монолит реально мешает) │ ▼ ┌─────────────────────────────────────────────┐ │ 1. Размер команды: сколько разработчиков? │ └─────────────────────────────────────────────┘ │ │ < 10 > 15-20 │ │ ▼ ▼ Модульный монолит ──────┼────── + │ code ownership по │ модулям ┌─────┴─────┐ │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌──────────┐ ┌──────────┐ │ ДАЛЬШЕ │ │ 2. Есть │ │ Модульн.│ │ НЕ ИДЁМ │ │ чёткие │ │ монолит │ │ — рано │ │ bounded │ │ + чёткие│ └─────────┘ │ contexts?│ │ границы │ └────┬─────┘ └─────────┘ │ │ ДА НЕТ │ │ ▼ ▼ ┌──────────┐ ┌──────────────┐ │3. Разные │ │ Сначала │ │ темпы │ │ наведите │ │ релиза у │ │ порядок в │ │ разных │ │ границах │ │ модулей? │ │ (глава 12) │ └────┬─────┘ └──────────────┘ ДА│ │ ▼ ┌──────────┐ │4. Есть │ │ команда │ │ на │ │ каждый │ │ bounded │ │ context? │ │ (или │ │ можем │ │ выделить)│ └────┬─────┘ ДА│ │ ▼ ┌─────────────────────┐ │5. Есть инфра- │ │ структура для │ │ микросервисов? │ │ (CI/CD, контейне- │ │ ризация, наблю- │ │ даемость) │ └────┬─────────────────┘ ДА│ │ ▼ ┌─────────────────────┐ │ ОК, ДУМАЙТЕ О │ │ МИКРОСЕРВИСАХ │ │ │ │ Но начинайте │ │ с 1-2 сервисов │ │ (Strangler Fig) │ └─────────────────────┘#Нюансы, которые не влезли в дерево
Когда НЕ надо переходить на микросервисы, даже если дерево говорит «да»:
- Ваш CTO никогда не работал с распределёнными системами. Серьёзно. Микросервисы без опыта — это распределённый монолит с задержками и сетевыми ошибками.
- У вас нет SRE или DevOps-инженера. Микросервисы требуют инфраструктуры. Если deploy — это
ssh && git pull, вы не готовы.- Продукт на стадии поиска product-market fit. Микросервисы замедлят вас в 2-3 раза. Стартапы умирают от отсутствия фич, а не от архитектурного несовершенства.
Когда можно нарушить «начинай с монолита»:
- Проект с самого начала проектируется как платформа для внешних разработчиков (API-first, изоляция с первого дня)
- Команда — 15+ опытных инженеров, которые уже делали микросервисы
- Это второй+ проект компании, и инфраструктура (CI/CD, observability, контейнеризация) уже готова
ПомнитеПомните: Легче сделать микросервис из модуля монолита, чем собрать монолит из микросервисов. Начинайте с монолита. Всегда.
#Чек-лист после выбора
Выбрали монолит:
- Разделили код на модули (bounded contexts) внутри монолита?
- Настроили хотя бы базовую observability — чтобы знать, что тормозит?
- Убедились, что модули не зависят друг от друга циклически?
Выбрали микросервисы:
- У каждого сервиса есть владелец (команда или человек)?
- Описан контракт (API) между сервисами до начала реализации?
- Настроен Circuit Breaker и Bulkhead на каждый external call?
- Есть correlation ID через все сервисы (трейсинг)?
- CI/CD пайплайн деплоит каждый сервис независимо?
- Вы готовы к тому, что distributed join — это code в приложении, а не SQL?
#A.2 SQL vs NoSQL — Decision Guide
#Быстрый ответ (если нет времени)
Начинайте с PostgreSQL. Серьёзно.Начинайте с PostgreSQL. Серьёзно. PostgreSQL покроет 80% ваших задач. NoSQL берите только когда PostgreSQL упрётся в конкретные ограничения.
Не выбирайте MongoDB «потому что схема поменяется». Схема поменяется в любом случае, а миграции — это нормально. PostgreSQL справляется с эволюцией схемы уже 30 лет.
#Decision Tree
┌────────────────────────────────────────────┐ │ ВЫБОР БАЗЫ ДАННЫХ │ │ Задайте себе эти вопросы по порядку │ └────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────┐ │ ВАМ НУЖНЫ JOIN'ы? │ │ (Orders → Users → Products, │ │ связанные данные в разных таблицах) │ └───────────────────────┬───────────────────────┘ ДА │ НЕТ │ │ │ ▼ │ ▼ ┌──────────┐ │ ┌──────────────────┐ │ SQL │ │ │ ВАМ НУЖНЫ │ │ (всегда) │ │ │ ТРАНЗАКЦИИ? │ └──────────┘ │ │ (деньги, заказы, │ │ │ │ инвентарь — все │ │ │ │ что требует ACID) │ │ │ └────────┬───────────┘ │ │ ДА │ │ НЕТ │ │ │ │ ▼ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ ВОТ И ВСЁ │ │ ДАЛЕЕ │ │ ДАЛЕЕ │ │ — берите │ │ СМОТРИ │ │ СМОТРИ │ │ PostgreSQL│ │ │ │ │ └──────────┘ │ │ │ │ ▼ │ ▼ │ ┌────────────────┐ │ ┌────────────────┐ │ │ ВАМ НУЖНА │ │ │ ВАШ СЦЕНАРИЙ │ │ │ ГИБКАЯ СХЕМА? │◄┘ │ ЗАПИСИ >> │ │ │ (документы, │ │ ЧТЕНИЯ? │◄┘ │ контент, CMS, │ │ (events, logs, │ │ каталог с │ │ time-series, │ │ разными │ │ метрики) │ │ типами) │ └────────┬────────┘ └───────┬────────┘ ДА │ НЕТ │ │ │ ДА│ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌────────────┐ ┌──────────────┐ │ Document DB │ │ Column │ │ Key-Value │ │ (MongoDB, │ │ Family │ │ (Redis, │ │ Firestore) │ │ (Cassandra, │ │ DynamoDB) │ │ │ │ ScyllaDB) │ │ │ │ Или: PG + │ │ │ │ Для: сессии, │ │ JSONB — │ │ Для: логов, │ │ кэш, │ │ дешевле и │ │ событий, │ │ счётчики, │ │ проще │ │ метрик │ │ очереди │ └──────────────┘ └────────────┘ └──────────────┘#Decision Matrix: SQL vs NoSQL
Критерий SQL (PostgreSQL) Document (MongoDB) Key-Value (Redis/DynamoDB) Column (Cassandra) Связанные данные ЕСТЬ JOIN'ы Manual (больно) Нет Нет Транзакции ACID Полные Ограниченные (4.0+) Нет Нет Миграции схемы Нормально (migrations) Гибко, но потом боль Не нужны Column evolution ok Горизонтальное масштабирование Сложно (sharding) Отлично (native) Отлично (cluster) Отлично (native) Консистентность Strong Tunable Strong (single node) Eventual Чтение по ключу Быстро Очень быстро Молниеносно Очень быстро Сложные запросы Отлично (SQL) Агрегации (сложно) Только по ключу CQL (ограниченно) Администрирование Умеренно Умеренно Просто Сложно Размер данных До 10-50TB на ноду Сотни TB Всё в RAM (дорого) Петабайты Когда брать Default choice Разнородные данные Кэш, сессии, real-time Time-series, logs #Нюансы, которые не влезли в матрицу
SQL для гибкой схемы — это нормально: PostgreSQL с JSONB + GIN index даст вам 90% того, зачем берут MongoDB. Вы получите и гибкую схему, и JOIN'ы когда понадобятся, и транзакции. Единственный сценарий, где MongoDB реально выигрывает — когда объём данных не влезает в одну ноду и нужен native sharding.
Mythbuster: «NoSQL быстрее для всего»: Нет. PostgreSQL на одном сервере читает быстрее MongoDB на одном сервере для большинства запросов. NoSQL выигрывает на кластере. Если у вас один сервер — SQL быстрее.
Counter-intuitive: данные, которые кажутся «без схемы» Часто оказываются со схемой, просто вы её не формализовали. «У каждого документа своя структура» — это работает до тех пор, пока вы не начинаете делать отчёты или агрегации. А потом вы пишете скрипты миграции в MongoDB, которые выглядят точно как SQL миграции, только без типов.
#Чек-лист после выбора
- Я выбрал эту БД из-за её сильных сторон, а не потому что «она модная»?
- Я знаю, как делать миграции в этой БД?
- У меня есть план: что делать, когда данных станет больше, чем влезает на одну ноду?
- Мои разработчики работали с этой БД раньше? (если нет — заложите время на обучение)
- Я представляю, как делать бэкапы и point-in-time recovery?
#A.3 REST vs GraphQL vs gRPC — Decision Matrix
#Быстрый ответ
RESTREST — для внешних API (клиенты, партнёры, мобилки если не сложные). GraphQL — для сложных клиентов (разные данные на одном экране, mobile с лимитированным bandwidth). gRPC — для internal микросервисов (высокая нагрузка, стриминг, строгие контракты).
#Decision Tree
┌─────────────────────────────────────────────────────┐ │ API ДЛЯ КОГО? │ └─────────────────────────────────────────────────────┘ │ │ EXTERNAL INTERNAL (клиенты, партнёры, (между сервисами) публичное API) │ │ ▼ ▼ ┌──────────────────┐ ┌────────────────────────┐ │ Клиенты какие? │ │ НУЖЕН СТРИМИНГ? │ └──────────────────┘ │ (real-time данные, │ │ │ большие файлы, │ ┌────┴────┐ │ bi-directional ком- │ │ │ │ муникация) │ ┌───▼──┐ ┌───▼──┐ └────────┬────────────────┘ │ Про- │ │ Раз- │ ДА │ НЕТ │ стые │ │ ные │ │ │ │ кли- │ │ эк- │ ▼ ▼ │ енты │ │ раны,│ ┌────────────┐ ┌────────────┐ │ CRUD │ │ mobile │ gRPC │ │ НУЖНА │ │ с од-│ │ раз- │ │ (bidirec- │ │ ВЫСОКАЯ │ │ ного │ │ ные │ │ tional │ │ ПРОИЗВОДИ- │ │ ре- │ │ зап- │ │ streaming) │ │ ТЕЛЬНОСТЬ? │ │ сурса│ │ росы │ └────────────┘ │ (миллионы │ └──┬───┘ └──┬───┘ │ rps, micro-│ │ │ │ секунды) │ ▼ ▼ └─────┬──────┘ ┌──────┐ ┌──────┐ ДА │ НЕТ │REST │ │Graph-│ │ │ │(про- │ │QL │ ▼ ▼ │ще не │ │(гиб- │ ┌────────┐ ┌────────┐ │надо) │ │кость)│ │ gRPC │ │ REST │ └──────┘ └──────┘ │ (proto │ │ (для │ │ │ │ конт- │ │ про- │ │ │ │ ракты,│ │ стоты)│ │ │ │ бинар-│ └────────┘ │ │ │ ный │ │ │ │ прото-│ │ │ │ кол) │ │ │ └────────┘#Decision Matrix
Критерий REST GraphQL gRPC Over-fetching Есть (лишние поля) Нет (просишь что нужно) Нет (schema-driven) Under-fetching Есть (N+1 запросов) Нет (всё в одном) Зависит от схемы Кэширование Отличное (HTTP cache, CDN) Сложное (persisted queries help) Есть, но не HTTP Tooling / Ecosystem Огромное (всё есть) Отличное (Apollo, Relay) Хорошее (protoc, buf) Latency Средняя (+headers) Средняя (+parse query) Низкая (binary, HTTP/2) Throughput Средний Средний Высокий Streaming SSE / WebSocket (надстройка) Subscriptions (сложно в production) Native (все 4 типа) Schema enforcement OpenAPI (опционально) Strong (schema-first) Strong (proto-first) Versioning URL или Header Evolve schema (осторожно) Proto compatibility (best) Learning curve Низкий Средний Высокий (protobuf, codegen) Browser support Нативное Отличное (через HTTP) grpc-web (с ограничениями) Debugging curl, Postman, браузер GraphiQL, Apollo Studio grpcurl, реже инструменты Type safety OpenAPI → код (генерация) Из коробки (typing) Из коробки (сильные типы) Когда брать Default для внешних API Сложные клиенты, mobile Internal, high perf, streaming #Когда каждое решение ломается
REST ломается когда:
- Клиенту нужно собирать данные из 5+ endpoints для одной страницы
- У вас мобильное приложение с лимитированным bandwidth — каждый лишний байт считается
- Часто меняющиеся требования к данным на клиенте (каждую неделю новые поля)
GraphQL ломается когда:
- Вы делаете простое CRUD API для одного клиента (оверхед не оправдан)
- У вас сложное кэширование (CDN не поможет, нужно Apollo Cache или своё)
- Клиенты пишут неэффективные запросы (N+1 на уровне клиента, глубинная вложенность) — вам придётся вводить cost analysis и depth limiting
- У вас нет выделенного бекенд-инженера на GraphQL (bff-layer — это работа)
gRPC ломается когда:
- Ваш API вызывается из браузера (gRPC-web — это компромисс)
- У вас маленькая команда без опыта protobuf (каждая ошибка в proto — перегенерация кода)
- Вам нужно быстро прототипировать (изменение proto → кодогенерация → деплой — медленнее, чем изменить JSON-ответ)
- Клиенты — не ваши сервисы (публичное API на gRPC — редкий и спорный выбор)
#Чек-лист после выбора
- Выбранный протокол поддерживается всеми клиентами (браузер, mobile, server)?
- Вы знаете, как будете версионировать API?
- У команды есть опыт с этим протоколом или заложено время на обучение?
- Вы проверили, что средство кэширования (CDN, API gateway, application cache) работает с этим протоколом?
- Streaming нужен прямо сейчас или "на будущее"? (не стройте для гипотетических сценариев)
#A.4 Сравнение NoSQL-систем — когда что
#Decision Tree
┌────────────────────────────────────────────────────┐ │ ВЫ УЖЕ РЕШИЛИ, ЧТО SQL НЕ ПОДХОДИТ │ │ (или SQL + NoSQL в разных ролях) │ └────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────┐ │ КАКОЙ ПРОФИЛЬ НАГРУЗКИ? │ └────────────────────────────────────────────────────┘ │ │ │ │ READ HEAVY WRITE HEAVY ОДИНАКОВО REAL-TIME (чтения >>> (записи >>> (сложные (низкая записей) чтений) запросы) латентность) │ │ │ │ ▼ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Нужна │ │ Какой │ │ Это time-│ │ Сессии, │ │ гибкая │ │ объём? │ │ series? │ │ кэш, │ │ схема? │ │ │ │ (логи, │ │ счётчики │ └────┬─────┘ └────┬─────┘ │ метрики, │ └──────────┘ ДА │ НЕТ │ │ events) │ │ │ │ ДА │ НЕТ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌────────┐ ┌────────┐ │ │ │ Redis │ │MongoDB │ │ <10TB │ ▼ ▼ │ (если │ │ (гиб- │ │ в год │ ┌────────┐ ┌────┴──┐ │ влезает │ │ кость) │ │ │ │Cassan- │ │Scylla │ │ в RAM) │ └────────┘ │ │ │dra │ │DB │ └─────────┘ ▼ │ │(про- │ │(быс- │ ┌────────┐ │ │верен, │ │трее, │ │DynamoDB│ │ │сложен) │ │слож- │ │ (про- │ │ └────────┘ │нее) │ │ стой, │ │ ┌────────┐ └───────┘ │ хоро- │ └─►│ │ │ шая │ │DynamoDB│ │ интег- │ │ (если │ │ рация │ │ AWS) │ │ с AWS) │ └────────┘ └────────┘#Matrix: когда какой NoSQL
Сценарий Рекомендация Почему Кэш, сессии, rate limiting Redis In-memory, атомарные операции, TTL Больше 10TB записей/год, логов Cassandra / ScyllaDB Write-optimised, horizontal scaling native Каталог товаров (разные типы) MongoDB / Firestore Гибкая схема, вложенные документы Высокоскоростные счётчики Redis (incr/decr) Атомарность, микросекунды Full-text поиск Elasticsearch / Meilisearch Инвертированные индексы, relevance scoring Графы (соцсети, рекомендации) Neo4j / DGraph Обход связей за константное время Serverless, low-ops DynamoDB / Firestore Не думать о серверах, auto-scale Time-series высокой плотности TimescaleDB (PG extension) / ClickHouse SQL-совместимость + columnar storage Глобальная мульти-регион DynamoDB Global Tables / Cassandra Multi-region из коробки #Нюансы
Redis — это не только кэш. У Redis есть строки, списки, сеты, хеши, sorted sets, streams, Geospatial индексы. Вы можете построить очередь, лидерборд, геопоиск и pub/sub на одной ноде Redis. Но он не влезает в RAM — всё, упираетесь в цену.
Cassandra — это боль. Cassandra даёт потрясающую write throughput и multi-region из коробки. Но модель данных в Cassandra нужно проектировать под запросы заранее. Вы не можете просто «добавить новый запрос» — скорее всего, придётся создавать новую таблицу (materialized views в Cassandra — зло, не используйте). Читайте про CQL и partition key design до того, как начнёте.
DynamoDB хорош, когда вы в AWS. Если вы не в AWS — DynamoDB не вариант. Его локальный аналог (DynamoDB Local) — для тестов, не для продакшена. На других облаках аналоги — Cosmos DB (Azure) или Bigtable (GCP), и они все разные.
#A.5 SPA vs SSR vs SSG — Decision Framework
#Быстрый ответ
Контентный сайт (блог, документация, лендинг)Контентный сайт (блог, документация, лендинг): SSG (Astro, Hugo, 11ty) — дёшево, быстро, безопасно. E-commerce, портал, контент с частыми обновлениями: SSR (Next.js, Nuxt, SvelteKit) — SEO + динамика. Приложение (дашборд, CRM, админка): SPA (React, Vue) — богатая интерактивность, не нужен SEO. Гибрид: SSR с ISR (Incremental Static Regeneration) — Next.js, свежий компромисс.
#Decision Tree
┌────────────────────────────────────────────────────┐ │ КОМУ НУЖЕН ЭТОТ САЙТ/ПРИЛОЖЕНИЕ? │ └────────────────────────────────────────────────────┘ │ │ ВНЕШНИЕ ПОЛЬЗОВАТЕЛИ INTERNAL (публичный сайт) (дашборд, админка, │ CRM, инструменты) │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ НУЖНО ЛИ SEO? │ │ Требуется │ └──────────────────┘ │ богатая │ │ │ интерактивность? │ ДА│ │НЕТ │ (drag&drop, │ │ │ │ real-time, │ ▼ │ │ сложное state) │ ┌────────┐ │ └────────┬──────────┘ │ Контент│ │ ДА │ │НЕТ │ стати- │ │ │ │ │ чен? │ │ ▼ ▼ │ (изме- │ │ ┌──────┐ ┌──────┐ │ няется │ │ │ SPA │ │SSR │ │ редко) │ │ │(React│ │(лег- │ └───┬────┘ │ │/Vue) │ │че, │ ДА │ │НЕТ│ └──────┘ │чем │ │ │ │ │SPA) │ ▼ │ │ └──────┘ ┌──────┐ │ │ │ SSG │ │ │ │ (ма- │ │ │ │ кси- │ │ │ │ маль-│ │ │ │ ная │ │ │ │ про- │ │ │ │ изво-│ │ │ │ ди- │ │ │ │тель- │ │ │ │ность)│ │ │ └──────┘ │ │ │ │ ▼ ▼ ┌──────────────────┐ │ Данные част- │ │ то меняются? │ │ (каждые 5 мин+) │ └────────┬─────────┘ ДА │ НЕТ │ │ ▼ ▼ ┌────────┐ ┌────────┐ │SSR │ │SSR │ │(каж- │ │+ ISR │ │дый ре- │ │(revali-│ │квест │ │date на │ │свежие │ │demand) │ │данные) │ │ │ └────────┘ └────────┘#Decision Matrix
Критерий SPA SSR SSG ISR Streaming SSR TTFB Быстро (CDN) Средне (server) Быстро (CDN) Быстро (CDN) Средне+ FCP / LCP Медленно (JS load) Быстро Быстро Быстро Очень быстро TTI Медленно Быстро Быстро Быстро Очень быстро SEO Плохо (с оговорками) Отлично Отлично Отлично Отлично Интерактивность Отлично Хорошо Хорошо Хорошо Хорошо Динамические данные Per request Per request Stale at build Revalidate периодически Per request Server cost Низкий (CDN) Средний Низкий (CDN) Средний Средний Build time Быстро Быстро Медленно (при масштабе) Медленно Быстро Cold start Нет Есть (server) Нет (CDN) Нет (CDN) Есть (server) UX при плохом интернете Плохо (пустой экран) Хорошо (HTML first) Отлично Отлично Хорошо Cache invalidation API-level API-level + HTML Full rebuild On demand + bulk API-level Сложность разработки Средняя Средняя+ (server/client) Низкая (plain HTML) Средняя Высокая (streaming) #Когда что брать — по типу проекта
Тип проекта Рекомендация Почему Документация, блог SSG (Astro, Hugo, Docusaurus) Статика, CDN, минимальные расходы Лендинг компании SSG или SSR + ISR SEO важен, контент обновляется редко E-commerce (каталог + корзина) SSR + ISR (Next.js) SEO для карточек, динамика для корзины Новостной портал SSR Контент обновляется постоянно, нужен SEO SAAS дашборд SPA (React/Vue) Сложная интерактивность, SEO не нужен Админка / CRM SPA Internal, SEO не нужен, сложные формы Соцсеть / лента SSR + SPA hydratation SEO для профилей, динамика для ленты Медиа-платформа Streaming SSR Быстрый first paint для контента #Нюансы, которые не влезли в дерево
«SPA не нужен SEO» — это уже не совсем правда. Google рендерит JavaScript с 2019 года. Но:
- Рендеринг JS занимает время — ваш контент может быть проиндексирован с задержкой
- Не все crawler'ы (Twitter, LinkedIn, Telegram) исполняют JS — превью ссылок не будет
- Core Web Vitals для SPA хуже — Google это учитывает в ранжировании
SSR — это не «SPA, который рендерится на сервере». Если вы просто добавили
renderToString()к React-приложению, и оно на клиенте загружает тот же JS — user experience может быть хуже, чем у чистой SPA. Пользователь видит HTML, потом JS загружается, потом происходит гидратация — это может вызывать визуальные скачки (flash of unmounted content).SSG — это не для всех. Если у вас сайт на 1M страниц, и каждая перестраивается за 1 секунду — билд будет длиться 11.5 дней. SSG хорош для сайтов до ~100K страниц. Дальше — ISR или SSR.
ISR — это компромисс, а не серебряная пуля. ISR даёт кэшированные страницы с периодическим ребилдом. Но:
- Stale page может показываться пользователям до следующего revalidate
- On-demand revalidation — вы должны явно вызывать API при изменении данных
- Если revalidate не успевает, пользователи видят старые данные
#Чек-лист после выбора
Выбрали SSG:
- Количество страниц позволяет перестраивать весь сайт за разумное время?
- Инкрементальные билды работают (не перестраиваем всё при изменении одной страницы)?
- Вы уверены, что данные не меняются между билдами?
Выбрали SSR:
- Сервер справляется с нагрузкой? (посчитайте одновременные запросы × время ответа)
- Настроено кэширование (HTTP cache, CDN, application cache)?
- Есть ли мониторинг серверной части (CPU, memory, request duration)?
Выбрали SPA:
- Настроен lazy loading модулей (code splitting)?
- Bundle size в пределах разумного (< 200KB initial)?
- SEO не требуется (или вы готовы к SSR workaround'ам)?
#A.6 Быстрый reference: типовые сценарии
Если у вас нет времени проходить все деревья — вот шпаргалка для самых частых ситуаций.
#Стартап (0-3 разработчика, product-market fit не найдён)
Решение Что брать Почему Архитектура Монолит Скорость важнее масштабирования База данных PostgreSQL 80% задач, транзакции, JSONB API REST (FastAPI / Express / ASP.NET) Простота, инструменты, экосистема Фронтенд SSR (Next.js / Nuxt) или SPA Зависит от продукта — но SSR даёт SEO Кэш Redis (один инстанс) Влезает в RAM, решает 90% задач кэша CI/CD GitHub Actions / GitLab CI Простота, не нужно отдельного сервера Хостинг Managed (Render, Railway, Fly.io) Не думать об инфраструктуре #Средняя компания (10-50 разработчиков, продукт растёт)
Решение Что брать Почему Архитектура Модульный монолит + первые микросервисы под нагрузкой База данных PostgreSQL + Redis + один NoSQL под сценарий API REST для внешних, gRPC для internal (под нагрузкой) Фронтенд SSR + SPA для админок Очереди Redis Streams или RabbitMQ (+ Kafka под event streaming) CI/CD GitHub Actions / GitLab CI + ArgoCD (GitOps) Хостинг Kubernetes (managed: EKS, GKE, AKS) — если есть DevOps #Enterprise (50+ разработчиков, высокая нагрузка)
Решение Что брать Почему Архитектура Микросервисы по bounded contexts (но не мельче) База данных PostgreSQL + специализированные NoSQL под каждую нагрузку + ClickHouse для аналитики API gRPC для internal, REST/GraphQL для external Очереди Kafka для event-driven, RabbitMQ/SQS для задач CDN CloudFront / Cloudflare + Edge Workers Кэш Redis Cluster + CDN + application cache CI/CD ArgoCD / Flux (GitOps) + preview environments Observability OpenTelemetry + Jaeger + Prometheus + Grafana Последнее напоминаниеПоследнее напоминание: Архитектура — это про контекст. Decision tree — это трамплин для размышлений, а не замена решению. Самый опасный инженер — тот, кто следует best practices, не понимая, почему они best в его конкретном случае.
Задавайте вопрос «ПОЧЕМУ?» к каждой рекомендации. Если ответ «потому что так принято» — идите и проверяйте сами.