Introducing Event Sourcing
Событие sourcing (ES) и Command Query Ответственность Сегрегация (CQRS) часто упоминается вместе. Хотя ни один из них не обязательно подразумевает другого, вы увидите, что они дополняют друг друга. В этой главе представлены ключевые понятия, лежащие в основе источников событий, и приводятся некоторые указатели на потенциальную связь с шаблоном CQRS. Эта глава представляет собой введение; Глава 4, « CQRS и ES Deep Dive », исследует источник событий и его связь с CQRS более подробно.
Чтобы помочь понять источник событий, важно иметь базовое определение событий, которое отражает их основные характеристики:
- События случаются в прошлом. Например, «оратор был забронирован», «место было зарезервировано», «наличные деньги были отменены». Обратите внимание, как мы описываем эти события с использованием прошедшего времени.
- События неизменны. Поскольку события происходят в прошлом, они не могут быть изменены или отменены. Однако последующие события могут изменять или отрицать эффекты предыдущих событий. Например, «бронирование было отменено» — это событие, которое изменяет результат более раннего события резервирования.
- События — это односторонние сообщения. События имеют единственный источник (издатель), который публикует это событие. Один или несколько получателей (подписчиков) могут получать события.
- Как правило, события включают параметры, которые предоставляют дополнительную информацию о событии. Например, «Seat E23 был забронирован Алисой».
- В контексте источников событий события должны описывать деловые намерения. Например, «Seat E23 был забронирован Алисой» описывает в деловых терминах, что произошло, и более описательно, чем «В таблице заказов строка с ключом E23 имела поле имени, обновленное значением Alice».
Мы также предположим, что события, обсуждаемые в этой главе, связаны с агрегатами; см. главу « CQRS в контексте » для описания условий DDD: агрегатов, совокупных корней и сущностей. Существуют две особенности агрегатов, которые имеют отношение к событиям и источникам событий:
- Агрегаты определяют границы согласованности для групп связанных объектов; поэтому вы можете использовать событие, созданное агрегированием, чтобы уведомить заинтересованные стороны о том, что транзакция (согласованный набор обновлений) произошла в этой группе сущностей.
- Каждый агрегат имеет уникальный идентификатор; поэтому вы можете использовать этот идентификатор для записи того, какой агрегат в системе был источником определенного события.
В оставшейся части этой главы мы будем использовать термин aggregate для ссылки на кластер связанных объектов, которые рассматриваются как единица для целей изменения данных. Это не означает, что источник событий напрямую связан с подходом DDD; мы просто используем терминологию из DDD, чтобы попытаться сохранить некоторую согласованность на нашем языке в этом руководстве.
Что такое источник событий?
Событие sourcing — это способ сохранения состояния вашего приложения, сохраняя историю, которая определяет текущее состояние вашего приложения. Например, система управления конференцией должна отслеживать количество завершенных заказов на конференцию, чтобы она могла проверить, доступны ли еще места, когда кто-то пытается сделать новое бронирование. Система может хранить общее количество заказов для конференции двумя способами:
- Он может хранить общее количество заказов для конкретной конференции и корректировать этот номер, когда кто-то делает или отменяет бронирование. Вы можете представить количество заказов как целочисленное значение, хранящееся в определенном столбце таблицы, которая имеет строку для каждой конференции в системе.
- Он может хранить все события бронирования и отмены для каждой конференции, а затем подсчитывать текущее количество заказов, переигрывая события, связанные с конференцией, для которых вы хотите проверить текущее общее количество заказов.
Сравнение использования уровня ORM с источником событий
Рисунок 1
На рисунке 1 показан первый подход к сохранению общего количества резервирований. Следующие этапы соответствуют номерам на диаграмме:
- Менеджер процессов или пользовательский интерфейс выдает команду на резервирование мест для двух участников конференции с идентификатором 157. Команда обрабатывается обработчиком команд для типа агрегатов SeatsAvailability .
- При необходимости слой объектно-реляционного сопоставления (ORM) заполняет совокупный экземпляр данными. Уровень ORM извлекает данные путем выдачи запроса в таблицу (или таблицы) в хранилище данных. Эти данные включают в себя существующее количество резервирований для конференции.
- Обработчик команд вызывает бизнес-метод для экземпляра aggregate, чтобы сделать оговорки.
- SeatsAvailability совокупности выполняет свою логику домена. В этом примере это включает в себя расчет нового количества резервирований для конференции.
- ORM сохраняет информацию в агрегатном экземпляре в хранилище данных. Уровень ORM создает необходимое обновление (или обновления), которое должно быть выполнено.
Заметка: |
---|
Определение диспетчера процессов см. В главе 6 « Сага о Сагах ». |
На рисунке 1 представлено преднамеренно упрощенное представление о процессе. На практике отображение, выполняемое уровнем ORM, будет значительно более сложным. Вам также необходимо будет точно учитывать, когда должны выполняться операции загрузки и сохранения, чтобы сбалансировать требования согласованности, надежности, масштабируемости и производительности.
фигура 2
На рисунке 2 показан второй подход, использующий использование источников событий вместо уровня ORM и системы управления реляционными базами данных (RDBMS).
Заметка: |
---|
Вы можете решить реализовать хранилище событий с помощью РСУБД. Реляционная схема будет намного проще, чем схема, используемая уровнем ORM в первом подходе. Вы также можете использовать собственный магазин событий. |
Яна говорит: | |
---|---|
CQRS / ES позволяет легко менять свои технологии. Например, вы можете начать с хранилища событий на основе файлов для прототипирования и разработки, а затем переключиться на настольный магазин Microsoft Azure для производства. |
Следующий список шагов соответствует номерам на рисунке 2. Обратите внимание, что шаги один, три и четыре такие же, как для решения, использующего уровень ORM.
- Менеджер процессов или пользовательский интерфейс выдает команду на резервирование мест для двух участников конференции с идентификатором 157. Команда обрабатывается обработчиком команд для типа агрегатов SeatsAvailability .
- Агрегатный экземпляр заполняется запросом на все события, принадлежащие агрегированию SeatsAvailability 157.
- Обработчик команд вызывает бизнес-метод для экземпляра aggregate, чтобы сделать оговорки.
- SeatsAvailability совокупности выполняет свою логику домена. В этом примере это включает вычисление нового количества резервирований для конференции. Агрегат создает событие для записи эффектов команды.
- Система добавляет событие, в котором записываются две новые оговорки в список событий, связанных с агрегатом в хранилище событий.
Этот второй подход проще, поскольку он распределяется с уровнем ORM и заменяет сложную реляционную схему в хранилище данных гораздо проще. Хранилище данных должно поддерживать запросы на события по идентификатору агрегата и добавлению новых событий. Вам все равно придется учитывать оптимизацию производительности и масштабируемости для чтения и записи в хранилище, но влияние этих оптимизаций на надежность и согласованность должно быть намного легче понять.
Заметка: |
---|
Некоторые оптимизации для рассмотрения — использование моментальных снимков, поэтому вам не нужно запрашивать и воспроизводить полный список событий для получения текущего состояния агрегата и сохранения кэшированных копий агрегатов в памяти. |
Вы также должны убедиться, что у вас есть механизм, который позволяет агрегату перестраивать свое состояние, запрашивая его список исторических событий.
То, что вы также приобрели во втором подходе, — это полная история или контрольный журнал заказов и аннулирования конференции. Поэтому поток событий становится вашим единственным источником истины. Нет необходимости сохранять агрегаты в любой другой форме или форме, поскольку вы можете легко воспроизвести события и восстановить состояние системы в любой момент времени.
В некоторых доменах, таких как бухгалтерский учет, получение источников событий является естественным, устоявшимся подходом: системы учета хранят отдельные транзакции, из которых всегда можно восстановить текущее состояние системы. Приобретение событий может принести аналогичные выгоды другим доменам.
Гэри говорит: | |
---|---|
Дополнительные сведения об использовании событий в качестве механизма хранения см. В разделе « События как механизм хранения » Грега Янга. |
Почему я должен использовать источники событий?
До сих пор единственным оправданием, которое мы предложили для использования источника событий, является тот факт, что он хранит полную историю событий, связанных с агрегатами в вашем домене. Это важная функция в некоторых областях, таких как учет, где вам нужен полный контрольный журнал финансовых транзакций и где события должны быть неизменными. Как только транзакция произошла, вы не можете удалить или изменить ее, хотя при необходимости вы можете создать новую корректирующую или реверсивную транзакцию.
Основным преимуществом использования источника событий является встроенный механизм аудита, который обеспечивает согласованность данных транзакций и данных аудита, поскольку они представляют собой одни и те же данные. Представление через события позволяет вам восстановить состояние любого объекта в любой момент времени.
-Paweł Wilkosz (Консультативный совет клиентов)
В следующем списке описаны некоторые дополнительные преимущества, которые можно извлечь из использования источников событий. Значение индивидуальных преимуществ будет зависеть от того, в каком домене вы работаете.
- Производительность . Поскольку события неизменяемы, вы можете использовать операцию добавления только при сохранении. События также просты, автономные объекты. Оба эти фактора могут привести к повышению производительности и масштабируемости системы, чем к подходам, использующим сложные модели реляционных хранилищ.
- Упрощение . События — это простые объекты, которые описывают, что произошло в системе. Просто сохраняя события, вы избегаете осложнений, связанных с сохранением сложных объектов домена в хранилище реляционных данных; а именно, несоответствие объектно-реляционного импеданса.
Заметка: |
---|
«Еще одна проблема с наличием двух моделей заключается в том, что она требует больше работы. Необходимо создать код для сохранения текущего состояния объектов, и нужно написать код для генерации и публикации событий. Независимо от того, как вы собираетесь делать эти вещи не могут быть проще, чем только публикации событий, даже если у вас есть что-то, что сделало сохранение текущего состояния совершенно тривиальным, чтобы сказать хранилище документов, все еще есть усилия, направленные на то, чтобы вносить это в проект ». -Greg Young — Зачем использовать Event Sourcing? |
- Аудит следа . События неизменны и сохраняют полную историю состояния системы. Таким образом, они могут предоставить подробный контрольный журнал того, что произошло в системе.
- Интеграция с другими подсистемами . События обеспечивают полезный способ общения с другими подсистемами. Магазин событий может публиковать события для уведомления других заинтересованных подсистем изменений в состоянии приложения. Опять же, хранилище событий обеспечивает полную запись всех событий, опубликованных в других системах.
- Получение дополнительной стоимости бизнеса из истории событий . Сохраняя события, вы можете определить состояние системы в любой предыдущий момент времени, запросив события, связанные с объектом домена, до этого момента времени. Это позволяет вам отвечать на исторические вопросы из бизнеса о системе. Кроме того, вы не можете предсказать, какие вопросы бизнес может пожелать спросить об информации, хранящейся в системе. Если вы храните свои события, вы не отбрасываете информацию, которая может оказаться ценной в будущем.
- Производство и устранение неисправностей . Вы можете использовать хранилище событий для устранения неполадок в производственной системе, взяв копию хранилища производственных событий и воспроизведя его в тестовой среде. Если вы знаете время возникновения проблемы в производственной системе, вы можете легко воспроизвести поток событий до этого момента, чтобы точно наблюдать за тем, что происходит.
- Ошибки фиксации . Вы можете обнаружить ошибку кодирования, в результате чего система вычисляет неверное значение. Вместо того, чтобы исправлять ошибку кодирования и выполнять рискованную ручную настройку сохраненного элемента данных, вы можете исправить ошибку кодирования и воспроизвести поток событий, чтобы система правильно вычисляла значение на основе новой версии кода.
- Тестирование . Все изменения состояния в ваших агрегатах записываются как события. Поэтому вы можете проверить, что команда имела ожидаемый эффект для совокупности, просто проверяя событие.
«Источники событий могут также помочь со сложными сценариями тестирования , когда вам необходимо проверить , что данное действие вызвало определенный результат Это особенно актуально для отрицательных результатов, когда вам нужно проверить , что это действие было. Не вызвать результат, это часто не проверяется при написании тестов, но может быть легко реализовано, когда изменения записываются через события ».
-Alberto Población (Консультативный совет клиентов)
- Гибкость . Последовательность событий может быть спроецирована на любое желаемое структурное представление.
«Пока у вас есть поток событий, вы можете проецировать его в любую форму, даже в обычную базу данных SQL. Например, мой любимый подход заключается в том, чтобы проектировать потоки событий в документы JSON, хранящиеся в облачном хранилище».
-Ринат Абдуллин, Почему Event Sourcing?
Глава 4, « CQRS и ES Deep Dive» , более подробно обсуждает эти преимущества. Существует также много иллюстраций этих преимуществ в эталонной реализации, описанной в сопроводительном руководстве « Изучение CQRS и Event Sourcing» .
«Из опыта ORM ведут вас по пути структурной модели, в то время как ES ведет вас по пути поведенческой модели. Иногда один имеет смысл, чем другой. Например, в моем собственном домене (а не модели) я получаю интегрироваться с другими сторонами, которые отправляют много действительно неинтересной информации, которую мне нужно отправить позже, когда что-то интересное произойдет с моей стороны. Это по своей сути структурно. Помещение этих вещей в события было бы пустой тратой времени, усилий и пространства Контрастируйте это с другой частью домена, которая приносит много пользы, зная, что произошло, почему это произошло, когда это произошло или не произошло, когда время и исторические данные важны для принятия следующего бизнес-решения. Вводя это в структурное модель просит мир боли. Это зависит от вас, выбирайте его, выбирайте мудро и, прежде всего:сделайте свои собственные ошибки ».
-Yves Reynhout (Список рассылок консультантов CQRS)
Привлечение внимания к событиям
В предыдущем разделе описаны некоторые преимущества, которые вы можете реализовать, если решите использовать источники событий в своей системе. Тем не менее, есть некоторые проблемы, с которыми вам может потребоваться обратиться, в том числе:
- Представление. Несмотря на то, что источник событий обычно улучшает производительность обновлений, вам может потребоваться время, необходимое для загрузки состояния объекта домена, путем запроса хранилища событий для всех событий, связанных с состоянием агрегата. Использование моментальных снимков позволяет вам ограничить объем данных, которые нужно загрузить, поскольку вы можете вернуться к последнему снимку и воспроизвести события с этой точки вперед. См. Главу « CQRS и ES Deep Dive » для получения дополнительной информации о моментальных снимках.
- Versioning. В какой-то момент в будущем вам может понадобиться изменить определение определенного типа события или агрегата. Вы должны учитывать, как ваша система сможет обрабатывать несколько версий типа события и агрегатов.
- Запросы. Хотя легко загрузить текущее состояние объекта, переиздав поток событий (или его состояние в какой-то момент в прошлом), сложно или дорого выполнить запрос, например «найти все мои заказы, где общая стоимость превышает 250 долларов ». Однако, если вы реализуете шаблон CQRS, вы должны помнить, что такие запросы, как правило, выполняются на стороне чтения, где вы можете гарантировать, что вы можете создавать прогнозы данных, специально предназначенные для ответа на такие вопросы.
CQRS / Е.С.
Структура CQRS и источники событий часто объединяются; каждый из которых добавляет преимущество другому.
В главе 2 « Знакомство с шаблоном разделения сериализации запросов команд » предположили, что события могут стать основой синхронной синхронизации состояния приложения из хранилища данных на стороне записи в хранилище данных на стороне чтения. Помните, что обычно хранилище данных на стороне чтения содержит денормализованные данные, оптимизированные для запросов, которые выполняются против ваших данных; например, для отображения информации в пользовательском интерфейсе вашего приложения.
«ES — отличный образец, который можно использовать для реализации связи между тем, что пишет, и тем, что читает. Это ни в коем случае не единственный способ создать эту ссылку, но она является разумной и существует множество предшествующих уровней техники с различными формами журналов и журналов. Главным переломным моментом для того, является ли ссылка «ES», кажется, является ли журнал эфемерным или постоянным источником правды. Сама модель CQRS просто требует разделения между записью и прочитанной вещью, поэтому ES строго дополняет друг друга ».
-Clemens Vasters (Список рассылок консультантов CQRS)
«Источником событий является состояние модели домена, которая сохраняется как поток событий, а не как один моментальный снимок, а не о том, как синхронизировать команды и стороны запроса (как правило, с помощью подхода, основанного на публикации / подписке на основе сообщений). »
-Udi Dahan (Список рассылок консультантов CQRS)
Вы можете использовать события, которые вы сохраняете в хранилище событий, для распространения всех обновлений, сделанных на стороне записи, на сторону чтения. Сторона с чтением может использовать информацию, содержащуюся в событиях, для поддержания любых денормализованных данных, которые требуются на стороне чтения для поддержки ваших запросов.
Рисунок 3
Обратите внимание, как сторона записи публикует события после того, как они сохраняются в хранилище событий. Это позволяет избежать необходимости использования двухфазной фиксации, которая вам понадобится, если бы агрегат отвечал за сохранение события в хранилище событий и публикацию события на стороне чтения.
Обычно эти события позволят вам сохранять данные на стороне чтения в актуальном состоянии практически в режиме реального времени; будет наблюдаться некоторая задержка из-за транспортного механизма, а в главе 4 « CQRS и ES Deep Dive » обсуждаются возможные последствия этой задержки.
Вы также можете в любой момент перестроить данные на стороне чтения с нуля, переиздав события из хранилища событий на стороне записи. Возможно, вам понадобится сделать это, если по какой-то причине хранилище данных на стороне чтения перестало синхронизироваться или потому, что вам нужно было изменить структуру хранилища данных на стороне чтения для поддержки нового запроса.
Вам нужно быть осторожным, переигрывая события из хранилища событий, чтобы перестроить хранилище данных на стороне чтения, если другие ограниченные контексты также подписываются на одни и те же события. Перед воспроизведением событий может быть легко освободить хранилище данных на стороне чтения; возможно, не так просто обеспечить согласованность другого ограниченного контекста, если он увидит повторяющийся поток событий.
Помните, что шаблон CQRS не требует, чтобы вы использовали разные магазины со стороны чтения и записи. Вы можете решить использовать один реляционный магазин со схемой в третьей нормальной форме и набор денормализованных представлений по этой схеме. Однако события повторного воспроизведения — очень удобный механизм для повторной синхронизации хранилища данных на стороне чтения с хранилищем данных на стороне записи.
Автономный поиск событий
Вы можете использовать источник событий, не применяя шаблон CQRS. Возможность восстановления состояния приложения, ведения истории событий для новых бизнес-данных и упрощения части хранения данных приложения является ценной в некоторых сценариях. Однако в этом руководстве основное внимание уделяется использованию источников событий в контексте шаблона CQRS.
Магазины событий
Если вы используете источник событий, вам понадобится механизм для хранения ваших событий и возврата потока событий, связанных с экземпляром агрегата, чтобы вы могли воспроизвести события для воссоздания состояния агрегата. Этот механизм хранения обычно называют хранилищем событий.
Вы можете выбрать собственный магазин событий или использовать стороннее предложение, например, EventStoreДжонатана Оливера . Хотя вы можете реализовать небольшой магазин событий относительно легко, качество продукции, масштабируемое, представляет собой большую проблему.
В главе 8 « Эпилог: извлеченные уроки » обобщается опыт, накопленный нашей командой в нашем магазине событий.
Базовые требования
Как правило, при реализации шаблона CQRS агрегаты создают события для публикации информации другим заинтересованным сторонам, например, другим агрегатам, менеджерам процессов, читаемым моделям или другим ограниченным контекстам. Когда вы используете источник событий, вы сохраняете эти события в хранилище событий. Это позволяет использовать эти события для загрузки состояния агрегата путем повторения последовательности событий, связанных с этим агрегатом.
Поэтому, всякий раз, когда совокупный экземпляр вызывает событие, должны произойти две вещи. Система должна сохранять событие в хранилище событий, и система должна опубликовать событие.
Заметка: |
---|
На практике не все события в системе обязательно имеют подписчиков. Вы можете поднять некоторые события исключительно как способ сохранить некоторые свойства агрегата. |
Всякий раз, когда системе необходимо загрузить текущее состояние агрегата, он должен запросить хранилище событий для списка прошлых событий, связанных с этим агрегатным экземпляром.
Базовое хранилище
События не являются сложными структурами данных; как правило, у них есть стандартные метаданные, которые включают идентификатор связанного с ним экземпляра агрегата и номер версии, а также полезную нагрузку с деталями самого события. Вам не нужно использовать реляционную базу данных для хранения ваших событий; вы можете использовать хранилище NoSQL, базу данных документов или файловую систему.
Производительность, масштабируемость и согласованность
Сохраненные события должны быть неизменными и всегда считаться в том порядке, в котором они были сохранены, поэтому сохранение события должно быть простой операцией быстрого добавления в базовом хранилище.
Когда вы загружаете сохраненные события, вы загружаете их в том порядке, в котором они были первоначально сохранены. Если вы используете реляционную базу данных, записи должны вводиться с использованием идентификатора агрегата и поля, определяющего порядок событий.
Если совокупный экземпляр имеет большое количество событий, это может повлиять на время, которое требуется для повторного воспроизведения всех событий для перезагрузки состояния агрегата. Одним из вариантов рассмотрения в этом сценарии является использование механизма моментальных снимков. В дополнение к полному потоку событий в хранилище событий вы можете сохранить моментальный снимок состояния агрегата в какой-то момент времени. Чтобы перезагрузить состояние агрегата, вы сначала загружаете самый последний снимок, а затем воспроизводите все последующие события. Вы можете создать моментальный снимок во время процесса записи; например, путем создания моментального снимка каждые 100 событий.
Заметка: |
---|
Как часто вы должны делать снимки, зависит от характеристик вашего базового хранилища. Вам нужно будет измерить, сколько времени потребуется для воспроизведения разных длин потоков событий, чтобы определить оптимальное время для создания ваших снимков. |
В качестве альтернативы вы можете кэшировать сильно используемые экземпляры совокупности в памяти, чтобы избежать повторного воспроизведения потока событий.
Когда хранилище событий сохраняет событие, оно также должно публиковать это событие. Чтобы сохранить согласованность системы, обе операции должны быть успешными или сбой вместе. Традиционным подходом к этому типу сценария является использование распределенной двухфазной транзакции фиксации, которая объединяет операцию добавления хранилища данных и операцию публикации инфраструктуры обмена сообщениями. На практике вы можете обнаружить, что поддержка двухфазных транзакций ограничена во многих хранилищах данных и платформах обмена сообщениями. Использование двухфазных транзакций фиксации может также ограничить производительность и масштабируемость системы.
Заметка: |
---|
Обсуждение двухфазных транзакций фиксации и влияние на масштабируемость см. В статье « Ваш кофейник не использует двухфазную фиксацию» Грегора Хохпе. |
Одной из ключевых проблем, которые вы должны решить, если вы решите реализовать свой собственный магазин событий, является то, как добиться этой согласованности. Например, хранилище событий, построенное поверх хранилища таблиц Azure, может использовать следующий подход для поддержания согласованности между сохраняющимися и публикуемыми событиями: использовать транзакцию для записи копий события в два объекта в одном разделе в той же таблице; один объект хранит неизменяемое событие, которое составляет часть потока событий совокупности; другой объект хранит событие, которое является частью списка событий, ожидающих публикации. Затем у вас может быть процесс, который читает список событий до публикации, гарантирует публикацию этих событий хотя бы один раз, а затем после публикации удаляет каждое событие из ожидающего списка.
Дополнительный набор проблем, связанных с согласованностью, возникает, если вы планируете масштабировать хранилище событий на нескольких узлах хранения или использовать несколько авторов для записи в хранилище. В этом случае вы должны предпринять шаги для обеспечения согласованности ваших данных. Данные на стороне записи должны быть полностью согласованными, а не последовательными. Для получения дополнительной информации о теореме CAP и обеспечения согласованности в распределенных системах см. Следующую главу « A CQRS и ES Deep Dive ».
Original(english): https://msdn.microsoft.com/library/jj591559.aspx
Translation: Basic, need improvment
Status: Draft
Action: Vote to improve