Event sourcing и CQRS: архитектура событийных систем для микросервисов
Когда речь заходит о масштабируемых микросервисных системах, на первый план выходят вопросы: как хранить состояние, как восстанавливать данные после сбоев и как синхронизировать изменения между сервисами.
Одним из архитектурных паттернов, который помогает решить эти задачи, является Event sourcing в связке с CQRS (Command Query Responsibility Segregation).
Напоминание: что это за подходы
• Event sourcing — состояние системы восстанавливается не из «текущей фотографии» (таблицы в БД), а из истории событий, которые к этому состоянию привели.
• CQRS — разделение логики на две части:
• Command side — принимает команды, меняет состояние через генерацию событий.
• Query side — подписывается на события и строит удобные проекции для чтения.
Вместе эти подходы позволяют:
• Легко масштабировать чтение/запись, • Хранить полную историю изменений, • Откатывать или пересобирать состояние, • Гибко интегрировать новые сервисы через события.
Практические инструменты для Java
1. Axon Framework
Фреймворк, который «из коробки» даёт Event sourcing + CQRS.
• Управление агрегатами и событиями. • Поддержка snapshotting. • Axon Server (отдельный продукт) для хранения событий и маршрутизации.
Пример агрегата:
Здесь чётко видно разделение: команда → событие → изменение состояния.
Восстановление состояния через события и snapshot
Axon позволяет восстанавливать агрегат либо из всей цепочки событий, либо «срезом» через снапшот.
Пример использования snapshotting:
Axon автоматически создаёт снапшоты (например, каждые 100 событий). При восстановлении агрегата сначала подгружается последний snapshot, а потом только «дельта» событий после него. Это сильно ускоряет реплей и экономит ресурсы.
2. EventStoreDB
Специализированная база данных для хранения событий.
Как хранятся данные:
• Каждое событие — это append-only запись в поток (stream). • Поток соответствует агрегату или категории (например, order-123 или orders). • Событие хранится в виде JSON или protobuf с обязательными метаданными (eventId, eventType, created, metadata).
Пример записи:
В чём оптимизация под event sourcing:
• append-only модель (нет апдейтов, только добавления), что даёт высокую производительность и лёгкий реплей; • Подписки (catch-up и persistent subscriptions) позволяют обрабатывать события асинхронно и надёжно; • Оптимизированная запись в потоки с возможностью репликации и шардинга.
3. Kafka
Kafka — это не event store в чистом виде, но она идеально подходит для потоковой обработки событий и интеграции микросервисов.
Классическая схема:
• Command side публикует событие в топик Kafka. • Query side подписывается и обновляет проекции.
Важно про масштабирование:
• Kafka масштабируется по числу партиций.
• Каждая партиция читается только одним consumer в группе → если не продумать стратегию партиционирования, система упрётся в ограничение по throughput.
Это гарантирует, что все события одного агрегата попадут в одну партицию, сохраняя порядок.
Именно партиционирование определяет, насколько эффективно Kafka обрабатывает события параллельно. Поэтому архитекторы обычно заранее просчитывают, какое количество партиций закладывать и как распределять ключи.
Где чаще всего спотыкаются
1. Путают команды и события. Команда — это «намерение», событие — это «свершившийся факт». Если команда не может быть выполнена, событие не возникает.
2. Хранят только текущее состояние. Тогда теряется сама суть event sourcing: история. Восстановление после ошибок становится невозможным.
3. Не продумывают проекции. Query side нужен для быстрого чтения. Если сразу не продумать проекции, то нагрузка на систему возрастает, а ответы на запросы становятся медленными.
4. Ошибки с масштабированием Kafka. При неправильном партиционировании теряется параллелизм и нарушается порядок обработки событий.
Event sourcing + CQRS дают надёжную основу для построения микросервисных систем, где важны масштабируемость, история изменений и гибкая интеграция.
В экосистеме Java для этого уже есть зрелые инструменты:
• Axon Framework для полной реализации паттерна; • EventStoreDB как специализированный event store; • Kafka для потоковой обработки и интеграции.
Выбор зависит от задач: хранение истории агрегатов, масштабируемые подписки или интеграция между сервисами.
Хотите узнать больше? Изучите другие статьи из разделов: