Event Sourcing pattern

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

Контекст и проблема

Большинство приложений работают с данными, и типичный подход заключается в том, чтобы приложение поддерживало текущее состояние данных, обновляя его, когда пользователи работают с ним. Например, в традиционной модели создания, чтения, обновления и удаления (CRUD) типичным процессом обработки данных является считывание данных из хранилища, внесение в него некоторых изменений и обновление текущего состояния данных новыми значениями — часто используя транзакции, которые блокируют данные.

Подход CRUD имеет некоторые ограничения:

    • Системы CRUD выполняют операции обновления непосредственно с хранилищем данных, что может замедлить производительность и оперативность, а также ограничить масштабируемость из-за требуемых затрат на обработку.
    • В области совместной работы со многими параллельными пользователями конфликты обновления данных более вероятны, поскольку операции обновления выполняются на одном элементе данных.
    • Если нет дополнительного механизма аудита, который записывает детали каждой операции в отдельный журнал, история теряется.
    • Для более глубокого понимания  пределов подхода CRUD см. CRUD, Only When You Can Afford It .

Решение

Шаблон Sourcing Event определяет подход к обработке операций с данными, которые управляются последовательностью событий, каждая из которых записывается в хранилище только для добавления. Код приложения отправляет ряд событий, которые настоятельно описывают каждое действие, произошедшее в данных, в хранилище событий, где они сохраняются. Каждое событие представляет собой набор изменений в данных (например, AddedItemToOrder).

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

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

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

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

Шаблон Event Sourcing предоставляет следующие преимущества:

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

События — это простые объекты, описывающие некоторые действия, которые произошли, вместе с любыми связанными данными, необходимыми для описания действия, представленного событием. События не обновляются непосредственно в хранилище данных. Они просто записываются для обработки в соответствующее время. Это может упростить внедрение и управление.

События обычно имеют значение для эксперта домена, тогда как несоответствие объектно-реляционного импеданса может затруднить понимание сложных таблиц базы данных. Таблицы — это искусственные конструкции, которые представляют текущее состояние системы, а не события, которые произошли.

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

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

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

Приобретение событий обычно сочетается с шаблоном CQRS, выполняя задачи управления данными в ответ на события и материализовывая представления из сохраненных событий.

Проблемы и соображения

При принятии решения о реализации этого шаблона рассмотрите следующие моменты:

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

Для получения информации о возможной согласованности см. Руководство по совместимости данных.

Хранилище событий является постоянным источником информации, поэтому данные о событиях никогда не должны обновляться. Единственный способ обновить сущность для отмены изменения — добавить компенсирующее событие в хранилище событий. Если формат (а не данные) сохраняемых событий должен измениться, возможно, во время миграции может быть сложно объединить существующие события в магазине с новой версией. Возможно, потребуется повторить все события, внесенные в изменения, чтобы они соответствовали новому формату или добавляли новые события, которые используют новый формат. Рассмотрите возможность использования штампа версии для каждой версии схемы событий для сохранения как старых, так и новых форматов событий.

Многопоточные приложения и несколько экземпляров приложений могут хранить события в хранилище событий. Консистенция событий в хранилище событий имеет жизненно важное значение, равно как и порядок событий, которые влияют на конкретный объект (порядок, который происходит с объектом, влияет на его текущее состояние). Добавление временной метки к каждому событию может помочь избежать проблем. Другой распространенной практикой является аннотирование каждого события, полученного в результате запроса с инкрементным идентификатором. Если два действия пытаются одновременно добавить события для одного и того же объекта, хранилище событий может отклонить событие, соответствующее существующему идентификатору объекта и идентификатору события.

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

Длина каждого потока событий влияет на управление и обновление системы. Если потоки большие, подумайте о создании моментальных снимков с определенными интервалами, таких как определенное количество событий. Текущее состояние объекта может быть получено из моментального снимка и путем воспроизведения любых событий, которые произошли после этого момента времени. Дополнительные сведения о создании моментальных снимков данных см. В статье Снимок на веб-сайте Архитектуры корпоративных приложений Мартина Фаулера и Репликация мастер-субординации снимков .

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

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

Когда использовать этот шаблон

Используйте этот шаблон в следующих сценариях:

  • Когда вы хотите зафиксировать намерение, цель или причину в данных. Например, изменения в объекте клиента могут быть записаны в виде серии конкретных типов событий, таких как перемещенный дом , закрытая учетная запись или умерший .
  • Когда важно минимизировать или полностью избежать возникновения противоречивых обновлений данных.
  • Если вы хотите записать события, которые происходят, и сможете воспроизвести их, чтобы восстановить состояние системы, отменить изменения или сохранить журнал истории и аудита. Например, когда задача включает в себя несколько шагов, вам может потребоваться выполнить действия для возврата обновлений, а затем повторить несколько шагов, чтобы вернуть данные в согласованное состояние.
  • При использовании событий является естественной особенностью работы приложения и требует незначительных дополнительных усилий по разработке или внедрению.
  • Когда вам нужно отделить процесс ввода или обновления данных от задач, необходимых для применения этих действий. Это может быть связано с улучшением производительности пользовательского интерфейса или распространением событий для других слушателей, которые принимают меры, когда происходят события. Например, интеграция системы расчета заработной платы с веб-сайтом представления расходов, чтобы события, создаваемые хранилищем событий в ответ на обновления данных, сделанные на веб-сайте, потреблялись как на веб-сайте, так и в системе начисления заработной платы.
  • Если вы хотите, чтобы гибкость позволяла изменять формат материализованных моделей и данных сущности, если требования изменяются, или — при использовании в сочетании с CQRS — вам необходимо адаптировать модель чтения или представления, которые отображают данные.
  • При использовании в сочетании с CQRS и возможная согласованность приемлемы при обновлении модели считывания или приемлемом эффекте воздействия регидратирующих объектов и данных из потока событий.

Этот шаблон может оказаться непригодным в следующих ситуациях:

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

Пример

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

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

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

Последовательность действий для резервирования двух мест выглядит следующим образом:

  1. Пользовательский интерфейс выдает команду для резервирования мест для двух участников. Команда обрабатывается отдельным обработчиком команд. Часть логики, которая отделена от пользовательского интерфейса и отвечает за обработку запросов, отправленных как команды.
  2. Совокупность, содержащая информацию обо всех оговорках для конференции, строится путем запроса событий, которые описывают заказы и отмены. Этот агрегат вызывается SeatAvailabilityи содержится внутри модели домена, которая предоставляет методы для запроса и изменения данных в совокупности.
    Некоторые оптимизации для рассмотрения — использование моментальных снимков (так что вам не нужно запрашивать и воспроизводить полный список событий для получения текущего состояния агрегата) и поддерживать кэшированную копию совокупности в памяти.
  3. Обработчик команд вызывает метод, выставленный моделью домена, чтобы сделать оговорки.
  4. В SeatAvailabilityсовокупности записывается событие, содержащее количество зарезервированных мест. В следующий раз, когда агрегат будет применять события, все оговорки будут использованы для расчета количества мест.
  5. Система добавляет новое событие в список событий в хранилище событий.

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

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

Дополнительную информацию об этом примере можно найти в разделе «Представление событийного поиска» .

Следующие шаблоны и рекомендации могут также иметь значение при реализации этого шаблона:

  • Command Query Responsibility Segregation (CQRS) . Магазин записи, который обеспечивает постоянный источник информации для реализации CQRS, часто основывается на реализации шаблона Sourcing. Описывает, как изолировать операции, считывающие данные в приложении, из операций, которые обновляют данные с помощью отдельных интерфейсов.
  • Materialized View Pattern . Хранилище данных, используемое в системе, основанной на поиске событий, обычно не подходит для эффективного запроса. Вместо этого, общий подход заключается в том, чтобы генерировать предварительно заполненные представления данных через определенные промежутки времени или когда данные изменяются. Показывает, как это можно сделать.
  • Compensating Transaction Pattern . Существующие данные в хранилище источников событий не обновляются, вместо этого добавляются новые записи, которые переводят состояние объектов в новые значения. Чтобы обратить вспять изменение, используются компенсирующие записи, потому что невозможно просто изменить предыдущее изменение. Описывает, как отменить работу, выполненную предыдущей операцией.
  • Data Consistency Primer . При использовании источников событий с отдельным хранилищем чтения или материализованных представлений считанные данные не будут сразу согласованы, но это будет в конечном итоге последовательным. Обобщает проблемы, связанные с поддержанием согласованности по распределенным данным.
  • Руководство по разделению данных . Данные часто разбиваются на разделы при использовании источников событий для повышения масштабируемости, уменьшения конкуренции и оптимизации производительности. Описывает, как разделить данные на дискретные разделы и проблемы, которые могут возникнуть.
  • Сообщение от Грега Янга Зачем использовать Event Sourcing? .

Original(English): https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing

Translation: Basic, need improvement

Status: Draft

Action: Vote to improve

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.