Работа с данными в event-driven архитектурах
Для ряда задач от систем может потребоваться определять не только текущее состояние сущностей данных, но и восстанавливать порядок событий того, как они пришли в это состояние.
Это может пригодиться как для развития бизнеса, проведения аналитики, так и в целях безопасности и расследований инцидентов. В то же время многим современным компаниям требуется возможность обработки и анализа данных в режиме реального времени.
Подход к управлению данными Event Sourcing предлагает эффективное решение задач аудита, а потоковая обработка данных позволяет получать из данных максимум.
В этом уроке вы изучите паттерн Event Sourcing и познакомитесь с несколькими инструментами потоковой обработки данных.
Event Sourcing
Event Sourcing (ES) является, по сути, развитием и продолжением архитектурного паттерна Event-Driven Architecture.
✏️ Event Sourcing — это подход к управлению данными, в котором изменения, происходящие в бизнес-домене, сохраняются как события в журнале. При этом новая запись всегда самая последняя, а уже записанные события изменению не подлежат. То есть хранится не текущее состояние, а вся история изменений, на основании которых можно это состояние при необходимости собрать.
Такой подход обеспечивает бизнес более богатыми возможностями работы с данными, поскольку каждое событие сохраняется в виде отдельной записи. События можно воспроизвести в том порядке, в котором они произошли.
Event Sourcing полезен в ситуациях, когда сущности данных могут изменяться в процессе жизненного цикла и есть задача сформировать историю этих изменений в хронологическом порядке. Такая возможность может быть полезна как клиенту, так и бизнесу.
Например, в онлайн-сервисе редактирования фотографий клиенту было бы удобно видеть все действия, которые он совершал с фотографией, чтобы в любой момент иметь возможность «откатить» фотографию до нужного состояния. В то же время онлайн-магазину может быть полезно отслеживать все действия пользователя в корзине (например, для целей аналитики).
Ключевые понятия Event Sourcing
ES построен на ключевых принципах самого EDA и основывается на следующих понятиях:
- События. Как вы помните, события отражают факт какого-то изменения в прошлом: совершение покупки, добавление товара в корзину, его оплату. При этом не допускается изменять сами события и их порядок. Так как с точки зрения Event Sourcing события используются для хранения данных, они обычно содержат уникальные метаданные, такие как временная метка, уникальный идентификатор субъекта и т. д. Данные внутри события могут использоваться и при записи новых событий для формирования и валидации состояния, и при формировании моделей для чтения.
- Хранилище событий. Каждое произошедшее изменение записывается в базу данных в виде события. Обычно хранение ведётся в виде журнала, который позволяет только добавлять записи в конец. События в журнале изменить нельзя, но их последствия могут быть изменены более поздними событиями. Например, создание заказа, которое сопровождалось событием
order_placed, может быть отменено, что приведёт к событиюorder_canceled. - Потоки событий. События, относящиеся к определённому домену или объекту домена, объединяются в поток. Поток должен иметь уникальный идентификатор, представляющий конкретный объект. Например, поток событий по заказу с номером 123. Каждое событие имеет свою собственную уникальную позицию в потоке. Эта позиция обычно представлена числовым, инкрементным значением. Число может использоваться для определения порядка событий при извлечении состояния. Ещё его можно использовать для обнаружения проблем состояния гонок по аналогии с версиями (например, в SQL-базах данных).
- Представления. При проведении операций чтения или записи может потребоваться построить представление данных, отражающих состояние на текущий момент времени или на какой-то момент времени в прошлом. Представления строятся по принципу последовательного применения каждого события в порядке, в котором они расположены в журнале.
- Представления текущего состояния сущности могут строиться при выполнении операций записи нового события. Например, чтобы провести валидацию на возможность выполнения операции.
- Также представления строятся и для чтения. Как правило, в ES неизбежно рождается множество представлений для чтения, которые агрегируют события различных типов и отображают их состояния на разные моменты времени или последовательность их изменений. Данный подход имеет значительные пересечения с CQRS.
- Источники. Компоненты приложения при получении команды, то есть инструкции выполнить какое-то действие, порождают события, которые публикуются в хранилище событий.
- Подписчики. У каждого потока событий может быть от нуля до бесконечности подписчиков. Как правило, специализированные хранилища событий предоставляют возможность использования их в том числе в качестве «брокера», то есть дают возможность подписываться на события, сохраняемые в БД.
Плюсы использования Event Sourcing
1. Широкие возможности для работы с данными
Когда все изменения хранятся в виде отдельных записей, удобно:
- Воспроизводить состояния отдельных сущностей или даже целых систем на какой-то конкретный момент времени в прошлом, что открывает новые возможности для анализа и отладки систем.
- Выявлять действия, произошедшие за какой-то период времени. Например, список транзакций, прошедших за определённый час.
- Анализировать поведение пользователя исходя из последовательности событий, порождённых его действиями. Например, каждое действие пользователя в корзине товаров (добавление, удаление) может фиксироваться в виде отдельного события, что даёт впоследствии богатую базу для анализа бизнес-заказчику.
2. Возможность отката изменений
Воспроизведение состояний даёт возможность не только просматривать его. Благодаря хранению полной истории изменений Event Sourcing позволяет легко откатываться к предыдущим состояниям всей системы.
3. Отказоустойчивость
Восстановление системы после сбоев становится проще, так как можно точно повторить или отменить изменения на основе истории событий.
4. Аудит
Система, построенная на ES, ведёт учёт всех данных в виде неизменяемых событий. И так как в события может быть записана информация, которая требуется для расследований инцидентов, такое решение может быть использовано в качестве журнала аудита. В том числе для целей коллег из безопасности, комплаенс и антифрод.
5. Разделение записи и чтения по умолчанию
ES допускает использование различных моделей для обработки записи (события) и чтения данных и эффективно сочетается с паттерном CQRS. Это значит, что можно оптимизировать производительность и масштабирование.
При этом для моделей чтения могут использоваться любые типы хранилищ, которые наиболее эффективно решают конкретную задачу.
Вы наверняка спросите про ограничения этого подхода. Ограничение Event Sourcing заключается в сложности самого подхода. В целом это очень специфичная история, которая применяется довольно редко.
Типовые кейсы для применения Event Sourcing
- Финансовые системы, где требуется строгий аудит и важна возможность отслеживать каждую транзакцию.
- Системы управления заказами — для отслеживания изменений в заказах и их статусах.
- CRM- и ERP-системы, где важно иметь полный аудиторский трейл действий пользователей и изменений данных.
- Онлайн-игры — для восстановления состояния игры в случае сбоев или обеспечения возможности повторных проигрываний. Например, в шахматах.
Когда Event Sourcing использовать не стоит
- В приложениях с простой доменной моделью, где не требуется сложная логика взаимодействия или подробное ведение истории изменений, внедрение Event Sourcing может привести к ненужному усложнению.
- Проекты с ограниченными инфраструктурными или человеческими ресурсами могут столкнуться с трудностями при реализации и поддержке Event Sourcing. Хранилище событий и инфраструктура для обработки событий могут требовать значительных вычислительных и хранилищных ресурсов, глубокого понимания данного подхода разработкой. Это может быть недоступно для небольших команд.
- Избыточность для простых CRUD-сервисов. Для приложений, главным образом ориентированных на стандартные операции создания, чтения, обновления и удаления данных без сложной бизнес-логики или требований к аудиту, использование Event Sourcing может оказаться избыточным и внести дополнительную сложность без заметного улучшения функциональности или производительности.
При внедрении Event Sourcing важным этапом является выбор подходящей БД, которая отвечала бы всем потребностям. Давайте разберём подробнее.
Выбор базы данных
Специализированные хранилища событий
На рынке существуют специализированные БД для хранения событий, которые используют готовое коробочное решение в подходе ES. Из-за этого существенно отличаются от других SQL- и NoSQL-хранилищ. Примерами таких хранилищ можно назвать:
- EventStoreDB. Широко известное хранилище событий, разработанное специально для поддержки Event Sourcing. Поддерживает потоки событий, проекции (автоматическая обработка событий для создания агрегатов) и имеет встроенные возможности для управления разрешениями. Его главное преимущество заключается в оптимизации для работы с событиями и CQRS. Одновременно может выступать в качестве брокера событий, то есть предоставляет инструменты подписки на события.
- Axon Server — это специализированное хранилище событий, разработанное для работы с Axon Framework, фреймворком для создания микросервисных и монолитных приложений с использованием Event Sourcing и CQRS. Axon Server обеспечивает масштабирование, управление транзакциями и поддерживает концептуальную разделённость команд, событий и запросов. Также может выступать в качестве брокера событий.
- Apache Kafka. Хотя Kafka часто используется как система для обработки потоковых данных, она также может быть применена в качестве хранилища событий для архитектур на основе Event Sourcing. Kafka предлагает высокую пропускную способность, долговременное хранение событий и возможность их повторного чтения.
SQL- и NoSQL-хранилища
Тем не менее SQL- и NoSQL-базы данных также можно адаптировать под нужды Event Sourcing, но они не предназначены специально для этого подхода.
- SQL-базы данных (PostgreSQL, MySQL и т. д.). Могут хранить последовательности событий в таблицах, обеспечивая ACID-свойства транзакций. Главным преимуществом является зрелость технологии и привычная экосистема инструментов. Однако производительность может страдать при очень большом количестве событий из-за особенностей хранения и индексации данных.
- NoSQL-базы данных (MongoDB, Cassandra и т. д.) предлагают гибкость в хранении событий и масштабируемость. Например, MongoDB может использоваться для хранения событий в виде документов, что упрощает их сериализацию и десериализацию. Cassandra хороша для записи и чтения больших объёмов данных благодаря горизонтальному масштабированию. Однако может возникнуть сложность с обеспечением последовательности чтения и записи событий.
Сравнение баз данных
Выбор между специализированными хранилищами событий и адаптацией SQL- или NoSQL-баз для Event Sourcing должен опираться на конкретные требования проекта. Например, на ожидаемый объём данных, требования к производительности и готовность команды работать с новыми или уже знакомыми технологиями.
- ПроизводительностьСпециализированные хранилища событий оптимизированы под ES и обычно обеспечивают лучшую производительность для этой задачи, особенно при обработке больших потоков данных.
- ФункциональностьEventStoreDB и подобные системы предлагают расширенные возможности, напрямую поддерживающие концепции Event Sourcing, включая комплексную работу с потоками событий, проекциями и агрегатами.
- Гибкость и масштабируемостьNoSQL-решения хороши для масштабирования и обеспечения гибкости структуры данных. Kafka отлично подходит для масштабируемой обработки потоков в распределённых системах.
- Экосистема и инструментыSQL-базы могут выигрывать в части доступности инструментов и зрелости экосистемы, особенно если команда уже имеет опыт работы с такими системами.
В случае с сервисом «ОнлиСхемы» использование Event Sourcing будет оправдано, если бизнес предъявит расширенные требования к управлению и просмотру историчности объектов и связей. Например, построение полноценного таймлапса всей доски, где пользователь мог бы просматривать состояние доски в любой момент времени.
Использование ES и специализированного хранилища для сервиса досок могло бы облегчить данную задачу благодаря широким возможностям работы с данными через события. Таким образом сервис досок мог бы использовать EventStoreDB для сохранения событий об объектах и связей. На эти события, в свою очередь, могли бы подписаться заинтересованные компоненты: компонент для обработки запросов, который команда разработала в рамках применения паттерна CQRS, а также сервис нотификаций.
Использование Kafka в качестве хранилища
Kafka легко сможет обрабатывать события в больших количествах, которые превышают возможности специализированных хранилищ. Однако при использовании Kafka основная сложность заключается в чтении текущего состояния сущности, так как она не обладает возможностью получить события только по конкретной сущности. Например, если бы мы хотели посмотреть все транзакции по определённой банковской карте.
В Kafka можно только прочитать все события из партиции, начиная с заданной позиции. В то же время специализированные хранилища позволяют собирать события в потоки в разрезе конкретной сущности или процесса. В случае всё той же банковской карточки это позволяет эффективно вычитывать только нужные события.
Можно было бы по аналогии завести отдельную партицию в Kafka на каждый поток событий — это позволило бы эффективно запрашивать все события в разрезе конкретной сущности. Но, к сожалению, это не сработает, поскольку существует ограниченное количество партиций, которые Kafka может иметь в одном кластере.
Так мы приходим к ситуации, когда в одной партиции собраны события, принадлежащие большому количеству разных сущностей или процессов. В данном случае в одной партиции будут собраны события по сотням или даже тысячам банковских карт. Поэтому, чтобы построить представление одной сущности, мы вынуждены прочитать все события из партиции, откидывая те, что не относятся к нужной нам. Если нужно получить все транзакции по одной карте, мы будем вынуждены обработать для этого десятки тысяч других. Такой подход может существенно увеличить время выполнения операций на чтение. И чем больше публикуется и хранится событий, тем больше время чтения.
Единственным решением этой проблемы является использование CQRS, что, в свою очередь, не всегда обоснованно усложняет систему и привносит проблемы, которые мы разбирали в прошлом уроке.
Вывод такой — Kafka вполне может быть использована в качестве хранилища событий, но такое решение должно приниматься только после оценки всех потенциальных сложностей, которое оно несёт. И всё-таки более типовым является использование Kafka в качестве высокопроизводительного и отказоустойчивого брокера событий.
Вызовы Event Sourcing
- Восстановление состояния. В системах ES состояние объекта восстанавливается путём последовательного применения всех событий, произошедших с момента создания объекта. В системах с долгой историей изменений это может привести к значительным задержкам при восстановлении состояний объектов.
- Объём хранения. Поскольку каждое событие сохраняется и является неизменяемым, объём данных, хранящихся в системе, со временем может расти очень быстро, что приводит к увеличению затрат на хранение и ухудшению производительности баз данных.
- Чтение данных. Приложениям часто требуются данные в агрегированном виде, что в контексте ES требует обработки всех событий для создания этого агрегированного представления. Вычисления могут быть довольно затратными и увеличивать время отклика системы.
Решение проблем через Snapshotting
✏️ Snapshotting — это паттерн, при котором регулярно создаются и хранятся снимки состояний объектов на определённый момент времени. Вместо восстановления состояния объекта через обработку всех событий с момента его создания система может загрузить последний снимок и применить только события, произошедшие после его создания. Это значительно ускоряет процесс восстановления состояний и обращения к данным.
- Ускорение восстановления состояния. С использованием снимков система может быстро восстановить состояние объекта до последнего снимка и затем применить только недавние события.
- Оптимизация хранилища. Периодическое удаление старых событий, состояние которых уже зафиксировано в снимках, может помочь контролировать рост объёма хранилища данных.
- Улучшение производительности чтения. Snapshotting уменьшает количество операций, необходимых для агрегации данных по событиям, оптимизируя таким образом производительность чтения.
Внедрение Snapshotting
При внедрении паттерна Snapshotting в систему Event Sourcing важно определить оптимальную периодичность создания снимков. Это зависит от нескольких факторов, включая частоту изменений состояний в вашей системе и требования к производительности. Ключевым моментом является достижение баланса между затратами на хранение снимков и улучшением производительности восстановления состояний и чтения данных.
Итоги
- Event Sourcing — архитектурный шаблон, при котором все изменения состояния приложения сохраняются как события в журнале в хронологическом порядке. При этом каждая новая запись помещается в самый конец, а уже записанные события изменению не подлежат.
- Event Sourcing позволяет воспроизводить состояния отдельных сущностей или даже целых систем на какой-то конкретный момент времени в прошлом, что открывает новые возможности для анализа и отладки систем.
- Event Sourcing допускает использование различных моделей для обработки записи (события) и чтения данных и эффективно сочетается с паттерном CQRS. Это значит, что можно оптимизировать производительность и масштабирование.
- На рынке существуют специализированные БД для хранения событий, которые используют готовое коробочное решение в подходе ES. Базы данных SQL и NoSQL тоже можно адаптировать под нужды Event Sourcing, но они не предназначены специально для этого подхода. В качестве хранилища событий в этом подходе может быть использована и Kafka, но такое решение должно приниматься только после оценки всех потенциальных сложностей, которые оно несёт.