Event Sourcing — мощный архитектурный шаблон, при котором все изменения состояния приложения сохраняются в хронологическом порядке. Эти записи служат источником для получения текущего состояния и одновременно являются журналом аудита истории приложения. Такая архитектура способствует децентрализованному изменению и чтению данных, хорошо масштабируется и подходит для систем, уже работающих с обработкой событий или планирующих её внедрение.
Сущности и события
Системы обычно описываются как совокупность сущностей (entities), хранящих состояние, и событий (events), отражающих изменения этих сущностей. События часто инициируются командами (commands) от пользователей, фоновых процессов или внешних систем.
В отличие от многих архитектурных шаблонов, фокусирующихся на сущностях (их хранении, доступе и модификации), Event Sourcing ставит во главу угла события. Вместо хранилища сущностей (например, реляционной базы данных или NoSQL), используется последовательный журнал всех событий.
<img src=»placeholder_image.jpg» alt=»Сравнение хранилища событий (Event Store) и хранилища сущностей (Entity Store)»>
Например, в хранилище сущностей банковский аккаунт хранится с текущим балансом (20 условных единиц). В Event Sourcing хранится история событий: начисление (100 условных единиц) и списание (80 условных единиц). Текущее состояние (баланс 20) вычисляется на основе этой истории.
Преимущества
Использование событий в качестве основной концепции имеет ряд преимуществ:
- Уменьшение несоответствия импеданса: Позволяет технологическим командам и бизнесу говорить на одном языке, так как бизнес часто мыслит событиями, а не сущностями.
- Разделение ответственности (CQRS): Позволяет оптимизировать запись и чтение данных независимо друг от друга.
- История изменений: Обеспечивает доступ к истории изменений системы, позволяя ответить на вопросы о её состоянии в прошлом.
Пример с банковским счётом
Рассмотрим пример с банковским счётом. Сущность — банковский счёт (Bank Account) с текущим балансом (20). Доступны команды: внесение денег (deposit) и снятие денег (withdraw). Бизнес-правило: снятие возможно, если сумма не превышает баланс.
События:
- Account credited: счёт пополнен.
- Account debited: средства списаны.
Последовательность команд:
- deposit amount 100
- withdraw amount 80
- withdraw amount 50 (эта команда не будет выполнена из-за недостаточных средств)
Журнал событий (Event Log) будет содержать только успешные операции:
<img src=»placeholder_image.jpg» alt=»Пример журнала событий»>
Для получения текущего баланса система обрабатывает события в хронологическом порядке. Каждое событие неявно имеет метку времени, позволяя вычислить состояние на любой момент времени.
В реальной системе потребуется:
- Хранение последовательности команд для трассировки событий.
- Журнал ошибочных событий для отслеживания неудачных операций.
- Возможно, производное хранилище (похожее на хранилище сущностей) для быстрого доступа к текущему состоянию, особенно при большом количестве событий.
<img src=»placeholder_image.jpg» alt=»Сравнение хранилища событий и производного хранилища»>
Реализация
Для Event Sourcing необходима реализация записи и чтения событий. Варианты хранилища событий:
- Файлы (простое решение).
- Брокеры сообщений, системы обработки потоков событий (для больших систем).
- Реляционные и документные базы данных (часто используются совместно с Event Sourcing).
В сложных системах применяются производные хранилища состояния для оптимизации чтения. Это соответствует принципу CQRS (Command Query Responsibility Segregation). Запросы направляются в производное хранилище, что позволяет оптимизировать его независимо от операций записи.
Проблемы и сложности
Несмотря на преимущества, Event Sourcing имеет сложности:
- Изменение мышления разработчиков: Необходимо сфокусироваться на событиях, а не сущностях.
- Неизменяемость событий: После записи события считаются неизменными.
- Полная информация в событиях: События должны содержать всю необходимую информацию для восстановления состояния.
- Повторная интерпретация событий: Системы и бизнес-правила могут меняться.
- Обработка ошибочных событий: Необходимо корректно обрабатывать и валидировать данные.
- Сложности интеграции с внешними системами.
- Масштабируемость: Горизонтальное масштабирование журнала событий может приводить к асинхронной обработке и проблемам согласованности данных (eventually consistent).
- Время обработки команд: Необходимо учитывать задержку между командой и записью события.
- Обработка ошибок команд.
- Обработка большого количества событий: Повторная обработка всего журнала событий может быть трудоёмкой. Периодические снимки состояния (snapshots) помогают в этом.
- Изменение структуры событий: Необходимо учитывать эволюцию структуры событий и обеспечить совместимость со старыми событиями.
Выводы
Event Sourcing — мощный подход с преимуществами в расширяемости и интеграции. Однако, необходимо учитывать сложности, связанные с моделированием, согласованностью данных, масштабируемостью и объёмом хранимых данных. Важно тщательно оценить применимость Event Sourcing к конкретной ситуации.