Поиск по сайту:

Как Event Sourcing помогает отслеживать состояние вашего приложения


Источник событий — это программная архитектура, в которой изменения состояния приложения регистрируются как серия постоянно сохраняемых «событий». В то время как большинство систем раскрывают только свое текущее состояние, источник событий создает полную запись всех предыдущих состояний.

Основы

Источники событий чаще всего используются в системах, основанных на временных или линейных процессах. Приложение для заказа в магазине может переводить транзакции между состояниями «ожидание», «одобрено» и «отправлено». Переход между каждым состоянием — это отдельное «событие» в жизненном цикле этого заказа.

Эта система может быть смоделирована с использованием традиционной реляционной базы данных:

order_id | order_status
---------|-------------
1000     | APPROVED

Однако в большинстве систем знания текущего статуса недостаточно. Вы также хотите видеть, когда заказ был утвержден. Мы могли бы сделать это, добавив дополнительные поля:

order_id | order_created_time | order_approved_time | order_shipping_time
---------|--------------------|---------------------|---------------------
1000     | 2021-04-30         | 2021-05-01          | NULL

Теперь ясно, что заказ был создан 30 апреля и утвержден 1 мая; его доставка все еще ожидается. Для многих приложений эта структура работает хорошо. Однако это может стать ограничительным по мере добавления новых состояний.

Давайте теперь посмотрим на этот пример, реструктурированный для использования источников событий:

event_id | event_order_id | event_type     | event_timestamp
---------|---------------------|----------------|----------------
2        | 1000                | order.approved | 2021-05-01
1        | 1000                | order.created  | 2021-04-30

Переходы состояния заказа были выделены в специальный журнал событий. В этой модели легко добавить новый тип события в будущем. Если клиенту необходимо отменить заказ, мы можем записать в журнал событие order.cancelled.

Источники событий также можно использовать для отслеживания данных о самом заказе. Вы можете добавить типы событий для order.apply_discount или order.process_refund. Вы можете записать событие в любое время, создав новое состояние для заказа, сохранив доступ к его предыдущим состояниям.

Реконструкция состояния объекта

Вы можете определить текущее состояние объекта, извлекая все связанные с ним события. Если вы хотите узнать, был ли одобрен заказ, проверьте, содержит ли его коллекция событий событие order.approved.

Вы можете реконструировать исторические состояния точно таким же образом. Если вам нужно узнать состояние заказа на конкретную дату, извлеките все события, зарегистрированные в этот день или ранее.

Источник событий — это инкрементная модель, которая автоматически отслеживает хронологию объекта. Каждое изменение состояния объекта должно фиксироваться как событие с отметкой времени. Когда вы используете источник событий, у вас есть автоматический контроль версий и полный журнал аудита переходов состояний.

Если вам когда-нибудь понадобится перестроить историю объекта, вы можете отказаться от любых событий, созданных после указанной даты. Представьте, что учетная запись пользователя была скомпрометирована злоумышленником, который предпринял различные действия от его имени. В системе, полностью основанной на событиях, вы можете удалить любые события, связанные с этим пользователем, в сомнительные сроки. Это восстановит их учетную запись до хорошего состояния.

Чтобы сохранить эту возможность, вы должны обеспечить неизменяемость событий. После того, как событие было зарегистрировано, вы никогда не должны изменять его свойства. Если событие требует корректировки, вы должны зарегистрировать другое событие, чтобы изменения вступили в силу.

Неизменяемость системы означает, что вы можете безопасно использовать ее для путешествий во времени и восстановления исторических состояний. Если вы хотите воспроизвести свою базу данных, какой она была два года назад, вы можете отбросить все события, зарегистрированные за прошедшее время.

Еще одним преимуществом источника событий является повышенная прозрачность состояния системы. Если пользователь сообщает об ошибке, вы можете клонировать базу данных, отбрасывать новые события и повторять их шаги с заведомо хорошей точки. Анализ зарегистрированных событий может помочь вам выявить ошибки в кодовой базе.

Поиск событий в реальном мире

Источники событий имеют репутацию сложных, громоздких и чрезмерно сложных процессов. Исторически источник событий был привязан к приложениям со строгими требованиями к аудиту и доказанной потребностью в воспроизведении событий.

Однако это не обязательно так. Если вы опытный разработчик, вы, вероятно, реализовали что-то близкое к источнику событий в прошлом. Любая система, которая ведет учет «событий», таких как попытки пользователя войти в систему, посещения страниц веб-сайта или этапы обработки заказов, естественным образом тяготеет к подходу, основанному на событиях, даже если вы не внедряете его намеренно.

Преднамеренная реализация источника событий в коде может быть громоздкой. Разработчики обычно предполагают, что данные, извлеченные из баз данных, являются точным представлением текущего состояния объекта. При использовании источника событий данные, которые вы извлекаете, сами по себе не представляют большой ценности. Вам нужно «воспроизвести» события в вашей кодовой базе, чтобы создать представление о состоянии.

Источники событий могут привести к снижению производительности. В приведенном выше примере для создания полного представления объекта заказа теперь требуется извлечь гораздо больше данных. В традиционной модели один выбор предоставляет все данные, связанные с заказом.

Есть и другие минусы. Если что-то пойдет не так, исправить источник событий будет сложнее. Вы не можете написать быстрый патч или вручную исправить базу данных. Вам нужно будет изящно изменить схемы событий, сохранив при этом историческую хронологию.

Объединение источников событий и CQRS

Источники событий обычно сочетаются с CQRS (разделение ответственности за запросы команд). Этот шаблон поддерживает разделение команд (которые записывают данные) от запросов (которые считывают данные).

Использование источника событий требует определенной степени CQRS. Данные записываются в вашу базу данных через события. Таким образом, модель записи чрезвычайно проста: это односторонний журнал происходящих событий, предназначенный только для добавления. События — это проявление «команд», описываемых CQRS.

Модель извлечения данных полностью независима. Вы можете использовать наиболее подходящую систему запросов для получения событий и наложения их на текущее состояние приложения. команды (события) переводят систему в новое состояние; запросы раскрывают аспекты состояния, которые запрашивает ваше приложение.

Иными словами, модели чтения и записи не осведомлены о том, как функционируют другие модели. Сущности в вашей кодовой базе (например, Заказ) хранятся в виде последовательности хронологических событий. Сохраненные события не могут быть регидрированы в сущности, к которой они относятся, без знания того, как получено состояние этой сущности. Эти знания реализуются в модели чтения (запроса), которая возвращает данные в ваше приложение.

Эта характеристика позволяет упростить уровень сохраняемости до вставки одной записи для каждой операции. Сохраненные данные не должны точно представлять какой-либо конкретный объект, поскольку модель запроса будет манипулировать ими позже. Это контрастирует с «традиционной» реляционной базой данных, где поля таблицы часто тесно связаны со свойствами объектов в вашей кодовой базе.

Результат модели запроса называется проекцией. Проекция — это представление данных с использованием точки зрения, отличной от их системы хранения. При использовании источника событий данные хранятся в виде потока событий, но проецируются в представление текущего состояния приложения. Это представление не имеет состояния, является неизменным и идемпотентным — создание представления никоим образом не изменяет базовые данные события.

Одной проекции может потребоваться взаимодействие с несколькими различными типами событий. Проекции рассматривают данные в совокупности таким образом, который имеет смысл для функций приложения. В нашем предыдущем примере проекция Order могла выводить объект, содержащий свойства CreatedDate, ApprovedDate и ShippedData, изучая события, связанные с субъектом заказа. .

Заключение

Поиск событий — это специализированная программная архитектура со встроенной поддержкой отчетности, хронологии и восстановления состояния. Шаблон может значительно упростить реализацию приложений, где эти качества желательны.

Источники событий также могут быть полезны в других сценариях, хотя при этом может встречаться убывающая отдача. Принятие источника событий требует, чтобы разработчики реализовали код для определения текущего состояния, добавляя накладные расходы, которые не возникают в системах, которые только хранят текущее состояние в своей базе данных.

Поиск событий лучше всего сочетается с другими методами архитектуры программного обеспечения, такими как CQRS, шаблон наблюдателя и согласованность в конечном итоге. Вам не нужно использовать источник событий во всем приложении — часто отдельные подмодули выигрывают от источника событий, в то время как основная часть вашего проекта использует традиционную реляционную базу данных.