Итак, в прошлых частях 1 и 2 я писал что обеспечение консистентности данных сильно мешает масштабированию. Что же делать на практике?
Здесь не будем касаться случаев ультра параллелизма и однонаправленных потоков данных вроде биржевых индикаторов по несколько миллионов событий в секунду, рассылаемые в реальном времени на сотни тысяч устройств. Разберем бизнес кейсы ближе к e-commerce, banking. Рассматриваем только data-bounded приложения, т.е. 90% случаев.
Я опущу длительные рассуждения (но могу рассказать в комментариях, если спросите) и представлю свою «поваренную книгу».
Также заранее извиняюсь за отсутствие конкретных кейсов, это сильно раздуло бы статью. Также предупреждаю, что при оформлении использовалась LLM, это упрощает форматирование, хотя некоторых может и раздражать. LLM проводилась также выверка терминов на соответствие многочисленным и размытым мнениям уважаемых авторов книг. Великоуважаемый Фаулер так и не смог дать внятного определения микросервисов.
ШАГИ
📌 1. Понять и измерить проблему в конкретном секторе
Определяем конкретную операцию или группу операций (например, оформление заказа, генерация отчётов, поиск товаров).
Измеряем текущие показатели: время отклика, пропускную способность, текущую нагрузку.
Это обязательно! Преждевременная оптимизация да еще в масштабах всего приложения - смертный грех архитектора!
📌 2. Устранять проблему в конкретном секторе
Не стараемся перепилить приложение целиком, это слишком дорого.
В сумме вырисовывается подход разделения на области высокой/полной консистентности (что затрудняет масштабирование) и области допустимой неполной / eventual consistency. В первом случае важна актуальность данных и отсутствие взаимных расхождений данных, упорядоченность данных и т.п. Во втором случае ценность актуальности или взаимной консистентности данных значительно ниже или уступает ценности оптимизации скорости в этой области.
Также можно нарисовать пути потоков данных, где важность времени ответа, это запрещает на пути разрезать БД и выносить в сеть взаимодействие.
Наложив области где типовые способы не справляются с повышением нагрузки на области и пути нежелательного разделения данных мы можем получить общее представление / визуальную карту по целесообразности и цене перехода на одну из распределенных архитектур.
Какие способы повышения масштабируемости следует рассмотреть и в каком порядке?
🟢 Дешёвые способы (простые, локальные оптимизации)
Оптимизация SQL-запросов и индексов
Кэширование (Redis, Memcached)
Простое горизонтальное масштабирование (реплики для чтения)
Простое шардирование (партиционирование таблиц в рамках одной БД)
Эти способы подробно описаны в 1-й части
🟡 Средние способы
Эти подходы требуют умеренных усилий и затрат, но при необходимости их можно относительно легко изменить или отменить:
Асинхронная обработка (очереди сообщений: Kafka, RabbitMQ)
Saga-паттерн (распределённые транзакции с компенсационными механизмами)
Средний способ, так как ввод компенсаций и отмены операции не сильно влияет на бизнес логику, ее не приходится переписывать полностью. Применяется, когда допустима eventual consistency с разумным временем рассогласованности данных и для бизнеса легко реализуемы обратные операции (отмена заказа к ним вряд-ли относится).
CQRS (особенно для аналитических и read-heavy сценариев):
Средний способ, так как CQRS для аналитики и чтения обычно прост и легко реализуем. Часто используется для разделения OLAP и OLTP задач. То есть этот паттерн легко просматривается исходя из назначения модуля.
Использование NoSQL-хранилищ (когда это не требует радикальной перестройки логики приложения):
Средний способ, если NoSQL-хранилище используется для простых задач (кэширование, хранение документов, логов, аналитических данных и т.д.), и не требует сложных транзакций и согласованности. Например, MongoDB для хранения документов, Elasticsearch для поиска, Cassandra для логов и аналитики — это средний уровень сложности.
🔴 Дорогие способы (сложные, труднообратимые, требуют значительных инвестиций и усилий)
Эти подходы применяются, когда средние способы исчерпаны, и требуют значительных изменений архитектуры и подходов к разработке:
Event Sourcing (как правило, сложный способ):
Дорогой способ, так как требует полной перестройки подхода к хранению данных и работы с ними.
Иногда (редко) может быть средним по сложности способом, если бизнес-процессы изначально событийно-ориентированы и уже изначально требуют такой подход. В большинстве случаев всё же Event Sourcing — это дорогой способ.
Полноценная микросервисная архитектура (с отдельными базами, инфраструктурой и сложной интеграцией)
Распределённые системы с географическим распределением и глобальной репликацией
Использование специализированных коммерческих решений (Oracle Exadata, SAP HANA, Google Spanner)
Сложные NoSQL-решения, когда они требуют радикального изменения логики приложения и подходов к согласованности:
Например, переход с реляционной БД на Cassandra для OLTP-транзакций с высокой нагрузкой и строгими требованиями к согласованности — это дорогое решение.
📌 Когда NoSQL относится к средним, а когда к дорогим решениям?
Средний способ:
NoSQL-хранилище используется для простых и понятных задач, например:Аналитика и логи (Elasticsearch, ClickHouse)
Документы и JSON-данные (MongoDB)
Кэширование и простые структуры данных (Redis)
Event-логи и временные ряды (Cassandra, InfluxDB)
Дорогой способ:
NoSQL-хранилище используется для замены основной транзакционной базы данных, требуя радикального изменения логики приложения, например:Переход с PostgreSQL на Cassandra для транзакций с высокой нагрузкой и сложными требованиями к согласованности и транзакционности.
Использование NoSQL в сценариях, где изначально была строгая транзакционная логика (банки, финансы, ERP-системы), и теперь приходится полностью перестраивать приложение.
📌 Пара слов про event sourcing
Event sourcing соперничает за 1 место по сложности с микросервисами. Его применение ограничено узкими бизнес областями.
Если ваш бизнес-домен уже событийно-ориентированный (например, банковские транзакции, учётные системы, системы аудита и бухгалтерии):
В таких случаях ES может оказаться естественным и логичным подходом.
Дополнительные затраты на проектирование и реализацию окупаются прозрачностью и гибкостью системы.
Также к серьезным минусам паттерна относится невозможность эволюционного перехода с простых паттернов, требуется переписать весь код, в отличие от эволюционных возможностей микросервисов. Поэтому Event Sourcing имеет смыл только если требуется аудит, реплей и прозрачность истории.
🎯 Итого:
Категория | Что входит |
---|---|
🟢 Дешёвые способы | SQL-оптимизация, кэширование, простое шардирование, реплики для чтения |
🟡 Средние способы | Асинхронность (Kafka/RabbitMQ), Saga-паттерн (компенсации), CQRS (особенно аналитика), отдельные БД, NoSQL (для простых и понятных сценариев) |
🔴 Дорогие способы | Event Sourcing (обычно), микросервисы, сложные распределённые системы, сложные NoSQL-решения, коммерческие специализированные решения |
📌 Что чаще всего отделяем от монолита (от общей БД)?
«Гонялки данных» — сервисы, которые:
✅ Не находятся на критическом пути бизнес-процессов (то есть, их временная недоступность или задержки не парализуют основной бизнес).
✅ Не требуют строгой консистентности и актуальности данных (могут работать с данными, которые слегка устарели, eventual consistency).
✅ Часто имеют ad-hoc характер (аналитика, отчёты, уведомления, интеграции с внешними системами и т.п.).
Такие сервисы действительно можно выделять в отдельные микросервисы, потому что их проблемы (сетевые задержки, временные сбои, eventual consistency) не повлияют критически на основной бизнес.
📌 Примеры таких «не критичных» микросервисов:
🔹 Сервисы аналитики и отчётности
Например, сервис, собирающий статистику, формирующий отчёты и BI-дашборды. Если он временно недоступен, основной бизнес-поток не пострадает.🔹 Сервисы отправки уведомлений (email, push, SMS)
Если уведомления задерживаются на несколько секунд или минут, это не критично для основной бизнес-логики.🔹 Сервисы интеграции с внешними системами
Например, выгрузка данных во внешние CRM, ERP, маркетинговые платформы. Если данные попадут туда чуть позже, это также не критично.🔹 Сервисы аудита и логирования
Сбор логов, аудит действий пользователей, мониторинг. Их временная недоступность не влияет на основной бизнес.🔹 Сервисы рекомендаций, персонализации и ML
Если сервис персонализации временно недоступен, пользователи просто получат стандартный контент без персонализации.
Под катом рисуночки для визуалов
Ниже представлены 5 архитектурных схем, которые действительно покрывают практически все (99.9%) типовые сценарии приложений. Эти схемы идут от простых (для большинства приложений) к сложным (для нагруженных, распределённых и сложных систем).
🚩 1. Монолит с единой БД (простая CRUD-система)
Подходит для:
Малых и средних проектов
Простых бизнес-приложений (админки, блоги, небольшие интернет-магазины)
Архитектурная схема:
+---------------------------+
| Монолитное приложение |
| (бизнес-логика, UI, API) |
+-------------+-------------+
|
v
+------------------+
| Одна база данных |
+------------------+
🚩 2. Монолит с Read-only репликами и кэшированием
Подходит для:
Средних приложений с умеренной нагрузкой на чтение
Большинства веб-приложений и интернет-магазинов среднего размера
Архитектурная схема:
+------------------------------+
| Монолитное приложение |
| (бизнес-логика, UI, API) |
+--------------+---------------+
|
v
+--------------------+
| Основная база (RW) +------+
+--------------------+ |
v (асинхронная репликация)
+-------------------------+
| Read-only реплики (RO) |
+------------+------------+
|
v
+-------------------+
| Кэш (Redis и т.п.)|
+-------------------+
🚩 3. Монолит (или модульный монолит) с асинхронными проекциями (CQRS-lite)
Подходит для:
Большинства приложений с умеренной или высокой нагрузкой на чтение
Систем с отчётами, аналитикой, поиском
Архитектурная схема:
+------------------------------+
| Монолитное ядро |
| (бизнес-логика, API команд) |
+--------------+---------------+
|
v (события через брокер)
+--------------------+
| Брокер сообщений |
| (Kafka, RabbitMQ) |
+---------+----------+
|
+--------+--------+--------+
v v v
+--------------+ +--------------+ +------------------+
| Read-only UI | | Аналитика | | Полнотекстовый |
| проекция | | и отчёты | | поиск (Elastic) |
+--------------+ +--------------+ +------------------+
🚩 4. Микросервисная архитектура с отдельными БД и eventual consistency
Подходит для:
Крупных приложений с высокой нагрузкой
Систем с независимыми командами разработки и разными доменами
Архитектурная схема:
+-------------+ +-------------+ +-------------+
| Микросервис | | Микросервис | | Микросервис |
| Заказы | | Платежи | | Склад |
+------+------+ +------+------+ +------+------+
| | |
v v v
+-------------+ +-------------+ +-------------+
| БД Заказов | | БД Платежей | | БД Склада |
+-------------+ +-------------+ +-------------+
| | |
+----------+-----------+-----------+----------+
| |
v v
+---------------+ +------------------+
| Брокер сообщений (Kafka, RabbitMQ и т.п.)|
+---------------+-------+------------------+
|
+----------+----------+
v v
+--------------+ +-----------------+
| Аналитика | | Read-only UI |
| и отчёты | | проекции |
+--------------+ +-----------------+
🚩 5. Event Sourcing + CQRS (для сложных систем с высокими требованиями к аудиту и истории)
Подходит для:
Финансовых систем, бухгалтерии
Систем с высоким требованием к аудиту и восстановлению состояния на любой момент времени
Высоконагруженных распределённых приложений с миллионами событий
Архитектурная схема:
+-------------------+ команды +------------------+
| +----------------->| |
| Клиенты (UI/API)| | Command Handler |
| |<-----------------+ |
+---------+---------+ ответы +--------+---------+
^ |
| события (через брокер) v
+---------+---------+ +-------------------+
| Read-only модели |<----------------| Event Store (лог) |
| (проекции) | события +-------------------+
+---------+---------+
|
v
+-------------------+
| Аналитика, отчёты |
+-------------------+
📌 Итоговая таблица выбора архитектуры:
Архитектура | Сложность | Нагрузка | Сложность логики | Команды разработки |
---|---|---|---|---|
Монолит + одна БД | Низкая | Низкая | Простая | 1 небольшая |
Монолит + реплики и кеш | Средняя | Средняя | Средняя | 1 небольшая |
Монолит + CQRS-lite (проекции) | Средняя | Средняя-Высокая | Средняя | 1-2 небольшие |
Микросервисы (отдельные БД) | Высокая | Высокая | Высокая | Несколько команд |
Event Sourcing + CQRS | Очень высокая | Очень высокая | Очень высокая | Несколько команд |
🚩 Итоговый вывод:
Эти 5 схем покрывают практически все возможные сценарии (99.9%) разработки приложений:
Монолит + одна БД — 50-60% приложений (простые CRUD)
Монолит + реплики и кеш — 20-30% приложений (средние нагрузки)
Монолит + CQRS-lite (асинхронные проекции) — 5-10% приложений (аналитика, сложные UI)
Микросервисы с отдельными БД — 3-5% приложений (крупные распределённые системы)
Event Sourcing + CQRS — около 1% приложений (финансовые, бухгалтерские, высоконагруженные системы с аудитом)
Замечу, что вероятности программисту попасть в конкретный тиа проекта совсем другие. Нужно учесть что 4-5 уровни сложности (если они обоснованы) применяются в крупных проектах и вероятность программисту попасть на 4-5 уровень близка к 20-30%
Таким образом, эти схемы закрывают практически все потребности, которые могут возникнуть в реальных проектах.
Напомню, что рассматриваются только усилия по повышению масштабируемости в системах массового обслуживания, data-bounded. Впрочем, это весьма распространенный класс задач. Иные архитектурные качества не рассматриваются: и с одним то качеством не сразу разберешься.
На этом серия статей по архитектурным паттернам заканчивается. В 1-ю часть я внес несколько добавлений по event sourcing.
Удачной архитектуры!
Дальше, видимо, будет серия по архитектурным паттернам борьбы со сложностью в больших проектах