что такое event sourcing

Почему Event Sourcing — это антипаттерн для взаимодействия микросервисов

И снова здравствуйте. В марте OTUS запускает очередной поток курса «Архитектор ПО». В преддверии старта курса подготовили для вас перевод полезного материала.

Последнее время получают распространение событийно-ориентированные архитектуры (event-driven architectures) и, в частности, Event Sourcing (порождение событий). Эта вызвано стремлением к созданию устойчивых и масштабируемых модульных систем. В этом контексте довольно часто используется термин “микросервисы”. На мой взгляд, микросервисы — это всего лишь один из способов реализации “ограниченного контекста” (Bounded Context). Очень важно правильно определить границы модулей и в этом помогает стратегическое проектирование (Strategic Design), описанное Эриком Эвансом в Domain Driven Design. Оно помогает вам идентифицировать / обнаружить модули, границы (“ограниченный контекст”) и описать, как эти контексты связаны друг с другом (карта контекстов, ContextMap).

События предметной области как основа единого языка

Хотя в книге Эрика Эванса это явно не обозначено, но события предметной области очень хорошо содействуют концепциям DDD. Такие практики как Event Storming Альберто Брандолини смещают акцент у событий с технического на организационный и бизнес-уровень. Здесь мы говорим не о событиях пользовательского интерфейса, таких как щелчок на кнопке (ButtonClickedEvent), а о доменных событиях, которые являются частью предметной области. О них говорят и их понимают эксперты в предметной области. Эти события представляют собой первоочередные концепции и помогают сформировать единый язык (ubiquitous language), с которым будут согласны все участники (эксперты в предметной области, разработчики и т.д.).

События домена, используемые для связи между контекстами

Доменные события могут использоваться для взаимодействия между ограниченными контекстами. Предположим, у нас есть интернет-магазин с тремя контекстами: Order (заказ), Delivery (доставка), Invoice (счет).

Рассмотрим событие “Заказ принят” в контексте Order. Контекст Invoice, а также контекст Delivery заинтересованы в отслеживании этого события, так как это событие инициирует некоторые внутренние процессы в этих контекстах.

Миф о слабой связанности

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

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

Это здорово, но сложность заключается в решении того, какие данные хранить в событии?

Простой ответ: Event Sourcing!

События полезны, так почему бы не использовать их по максимуму. Это основная идея Event Sourcing. Вы храните состояние агрегата не через обновление его данных (CRUD), а через применение потока событий.
Помимо того, что вы можете воспроизвести события и получить состояние, есть еще одна особенность Event Sourcing: вы бесплатно получаете полный журнал аудита. Поэтому, когда требуется такой журнал, при выборе стратегии хранения обязательно обратите внимание на Event Sourcing.

Event Sourcing — это только уровень хранения

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

… и вот моя точка зрения: Event Sourcing — это локальное решение, используемое только в одном ограниченном контексте! События Event Sourcing не должны выдаваться наружу во внешний мир! Другие ограниченные контексты не должны знать о способах хранения данных друг друга, и поэтому им не важно, использует ли какой-то контекст Event Sourcing.

Если вы используете Event Sourcing глобально, то вы раскрываете свой уровень хранения.

Способ хранения данных становится вашим публичным API. Каждый раз при внесении изменений в уровень хранения вам придется иметь дело с изменением публичного API.

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

Выход есть

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

Предлагаемое решение очень простое: независимо от того, какой подход вы используете для хранения данных (CRUD или Event Sourcing), вы публикуете доменные события в глобальном хранилище событий. Эти события представляют собой публичное API вашего контекста. При использовании Event Sourcing вы храните события Event Sourcing в своем локальном хранилище, доступном только для этого ограниченного контекста.

Свобода выбора

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

Есть два варианта для работы с ”событиями реального мира”: служба с открытым протоколом и общедоступным языком (Open Host Service, Published Language) или Заказчик / Поставщик (Customer/Supplier).

Служба с открытым протоколом и общедоступным языком (Open Host Service, Published Language)

Публикуется только одно доменное событие, содержащее все данные, которые могут понадобиться другим ограниченным контекстам. В терминологии DDD это можно назвать службой с открытым протоколом (Open Host Service) и общедоступным языком (Published Language).

Наступление события реального мира “Заказ принят” приводит к тому, что публикуется одно доменное событие OrderAccepted. Полезная нагрузка этого события содержит все данные о заказе, которые, могут понадобиться другим ограниченным контекстам… так что, надеюсь, контексты Invoice и Delivery найдут всю необходимую им информацию.

Заказчик / Поставщик (Customer/Supplier)

Для каждого потребителя публикуются отдельные события. Необходимо согласовать модели каждого события только с одним потребителем, не требуется определять общую разделяемую модель. DDD называет эти отношения Заказчик / Поставщик (Customer/Supplier).

Я не хочу сейчас обсуждать плюсы и минусы этих подходов. Я хочу просто обратить внимание на то, что можно выбирать количество доменных событий и данные, которые в них хранить.

Это преимущество, которое вы не должны недооценивать, потому что можно решить, как развивать API вашего ограниченного контекста без привязки к событиям Event Sourcing.

Заключение

Выставление наружу деталей хранения — хорошо известный анти-паттерн. Говоря о хранении, мы в первую очередь думаем о таблицах базы данных, но мы увидели, что события, используемые для Event Sourcing, являются лишь еще одним способом хранения данных. Поэтому выдавать их наружу тоже является анти-паттерном.


Перевод: “хороший разработчик как оборотень — боится серебряных пуль”.

Event Sourcing — это мощный подход, если используется правильно (локально). На первый взгляд кажется, что для событийно-ориентированных архитектур это серебряная пуля, но если присмотреться внимательнее, то видно, что этот подход может привести к сильной связанности … чего вы, конечно, не хотите.

Читайте также:  что делать если ноет кисть руки

Ссылки

Помимо моего личного опыта, я получил много вдохновения от различных статей и конференций. Я хотел бы отметить выступление Эберхарда Вольфа (Eberhard Wolff) “Event-based Architecture and Implementations with Kafka and Atom” (событийная архитектура и реализация с использованием Kafka и Atom). Особенно про Event Sourcing и про то, что такое события, что очень актуально в контексте этого поста. Пример с интернет-магазином также был вдохновлен этим докладом.

Если вы хотите получить больше информации, вы можете обратиться к этим ресурсам:

Источник

«А что если», Event Sourcing

Фундамент

«Печенюшки»

Естественно, раз подход применяется, значит, в нем есть то самое, «ОНО», ради чего игра стоит свеч.

История событий

Начнем с того, что Event Sourcing – это мечта для тех, кто хочет помнить все «как и почему», «что и когда», произошло в системе. Скажем, это аудит записи, только еще лучше за счет хранения информации «почему». Мне запоминался пример, который Greg Young использовал на одной из своих презентаций:

Банк хранит сведения о том, где проживают их клиенты и в один день происходит изменение одного из почтовых адресов. При хранении информации в таблице, мы максимум сможем увидеть, что запись обновилась. Если был подключен аудит, то в целом мы можем посмотреть на историю всех предыдущих значений. Но сможем ли мы узнать, почему она изменилась: может это была изначальная ошибка?, а может клиент переехал в другое место? С помощью Event Sourcing мы как раз и смогли бы ответить на этот вопрос, т.к. у нас было бы 2 события: КлиентПереехал, ИсправленаОшибкаАдреса.

Проекции

Естественно, раз вся полезная информация о сущности сохранена в потоке событий, то напрямую ей воспользоваться нельзя. Для UI нам необходимы плоские модели (проекции), которые мы как раз и создаем, используя обработчики событий, которые называются денормализаторы (denormalizers). Отличительным свойством можно назвать то, что такой обработчик можно менять и добавлять, как и когда угодно. В любой момент проекцию можно «выкинуть», проиграть все события от начала и новая готова. Небольшой пример кода:

Конечно, кроме проекций для интерфейса, денормализация событий – это мощное средство построения разнообразных отчетов и проведения анализа event sourced сущности.

UI ориентированный на процесс

Замена основного хранилища данных (source of Truth) с нормализованных данных в таблицах, и CRUD операций над ними, на записи о событии, подталкивает на изменения к подходу в дизайне. Не выгодно иметь события в стиле CRUD, т.к. тогда теряется весь смысл в их семантике и привязке к происходящему. Значит необходимо строить такой интерфейс, где пользователь выполняет небольшие атомарные действия над сущностью. Т.е. дизайн интерфейса должен быть workflow driven, а не data input-based.

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

Технические моменты

Проблемы

Как можно догадаться, они значительны.

Ад для рефакторинга

Итак, о чудо, наши события immutable, а их поток append-only. Хм, чтобы это значило? А значит это, что любое единожды происшедшее события необходимо поддерживать навечно. Их нельзя ни отменить, ни удалить. Можно создавать новые версии событий, события которые компенсируют ошибки, — а это все новые и новые классы, которые необходимо добавить в имеющиеся обработчики и пронести по всей цепочке использования сущности. Это все равно, что отнять право на ошибку у программиста, и никакого тебе “rollback” плана. Разве что вернуть базу событий к предыдущему снэпшоту, потеряв все действия пользователей, если конечно вы на такое будете согласны 🙂

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

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

Нет готовых инструментов

Начиная разработку используя Event Sourcing, будьте готовы к тому, что все инструменты для работы с событиями придется написать самим. Конечно, есть EventStore от Jonathan Oliver, но это лишь малая часть. Вам понадобится графический и программный интерфейс их управлением: просмотра и поиска, построения и обновления проекций, создания снэпшотов для оптимизации чтения и др.

Взаимно компенсирующие события

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

Поддержка

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

Выводы

Как сказал один мой знакомый: «Мы не решаем проблемы, а переносим их в более комфортную для себя плоскость». Также можно сказать и про Event Sourcing. Каждый должен определить, нужен ли он для решения конкретной задачи, или нет.

Я же могу сказать следующее: используйте Event Sourcing для хорошо изученных сущностей и точечно, не пытайтесь построить на нем всю систему. Желательно, когда это не первая, а может и не вторая ее реализация.

Источник

Знакомимся с Event Sourcing. Часть 1

Перевод статьи подготовлен в преддверии старта курса «Java Developer. Professional».

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

Что такое Event Sourcing

Эксперты предметной области обычно описывают свои системы как совокупность сущностей (entity), представляющих собой контейнеры для хранения состояния и событий (event), отображающих изменения сущностей в результате обработки входных данных в рамках различных бизнес-процессов. Часто события инициируется командами (command), исходящими от пользователей, фоновых процессов или интеграций с внешними системами.

По сути, Event sourcing фокусируется на событиях, связанных с изменениями в системе.

Читайте также:  что значит причастили и соборовали

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

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

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

Ниже показано сравнение хранилища событий (Event Store) с хранилищем сущностей (Entity Store) (подробнее будет описано далее):

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

Принцип работы Event Sourcing

Рассмотрим простой пример с банковским счетом. У нас будет сущность (entity), представляющая собой банковский счет (Bank Account). Для простоты сделаем только один счет без его идентификации с помощью номера счета или каким-либо другим способом. Счет будет хранить текущий остаток средств.

Для счета будут доступны две команды (command): внести деньги (deposit) и снять деньги (withdraw). В командах будет указываться сумма для внесения или снятия. Также определим бизнес-правило, которое проверяет, что команда на снятие средств может быть обработана только в том случае, если запрашиваемая сумма равна или меньше текущего остатка на счете.

При таком подходе можно выделить два события (event) — «Account Credited» (Счет пополнен) и «Account Debited» (Средства списаны со счета). В этих событиях есть информация о сумме (amount), которая была внесена или снята. Здесь можно было бы упростить до одного события с положительной или отрицательной суммой, но в данном примере мы их разделим.

На диаграмме ниже показана модель данных.

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

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

1. deposit < amount: 100 >— внести 100
2. withdraw < amount: 80 >— снять 80
3. withdraw < amount: 50 >— снять 50

Самая простая реализация Event Sourcing требует журнала событий (event log), который представляет собой просто последовательность событий. При обработке команд, приведенных выше, получится такой журнал.

Третья команда не может быть выполнена, так как запрошенная сумма превышает доступный баланс.

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

Это законченный (хоть и тривиальный) пример Event Sourcing. В реальной системе, скорее всего, этот пример придется расширить.

Возможно, потребуется сохранять последовательность команд, чтобы была возможность идентифицировать, как возникло событие, а также сделать отдельный журнал «ошибочных событий» (error event), в который записывать команды, которые не удалось выполнить, для дальнейшей обработки ошибок и ведения полной истории успешных и неуспешных команд.

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

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

Очевидно, что по сравнению с полноценным хранилищем событий (Event Store), это очень примитивный пример. И это одна из причин, по которой многие разработчики используют только хранилище сущностей. В этом случае текущий остаток на счете доступен сразу и нет необходимости обрабатывать все исторические события.

Однако Event Sourcing не исключает хранилища сущностей. Часто хранилища сущностей присутствуют и в Event Sourcing — проектах.

Источник

Введение в CQRS + Event Sourcing: Часть 1. Основы

В первый раз я услышал о CQRS, когда устроился на новую работу. В компании, в которой работаю и по сей день, мне сразу сказали что на проекте, над которым я буду работать используется CQRS, Event Sourcing, и MongoDB в качестве базы данных. Из этого всего я слышал только о MongoDB. Попытавшись вникнуть в CQRS, я не сразу понял все тонкости данного подхода, но почему-то мне понравилась идея разделения модели взаимодействия с данными на две — read и write. Возможно потому что она как-то перекликалась с парадигмой программирования “разделение обязанностей”, возможно потому что была очень в духе DDD.

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

Начнем с CQRS

Что же такое CQRS?
CQRS расшифровывается как Command Query Responsibility Segregation (разделение ответственности на команды и запросы). Это паттерн проектирования, о котором я впервые услышал от Грега Янга (Greg Young). В его основе лежит простое понятие, что вы можете использовать разные модели для обновления и чтения информации. Однако это простое понятие ведет к серьёзным последствиям в проектировании информационных систем. (с) Мартин Фаулер

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

Вот как я изобразил схему работы CQRS

Первое что бросается в глаза это то что у вас уже две модели данных, одна для чтения (Queries), одна для записи (Commands). И обычно это значит что у вас еще и две базы данных. И так как мы используем CQRS + Event Sourcing, то write-база (write-модель) — это Event Store, что-то вроде лога всех действий пользователя (на самом деле не всех, а только тех которые важны с точки зрения бизнес-модели и влияют на построение read-базы). А read-база — это в общем случае денормализировнное хранилище тех данных, которые вам нужны для отображения пользователю. Почему я сказал что read-база денормализированная? Вы конечно можете использовать любую структуру данных в качестве read-модели, но я считаю что при использовании CQRS + Event Sourcing не стоит сильно заморачиваться над нормализвацией read-базы, так как она может быть полностью перестроена в любое время. И это большой плюс, особенно если вы не хотите использовать реляционные базы данных и смотрите в сторону NoSQL.
Write-база вообще представляет собой одну коллекцию ивентов. То есть тут тоже нету смысла использовать реляционную базу.

Читайте также:  что значит лежащий андроид с восклицательным знаком

Event Sourcing

Идея Event Sourcing в том чтобы записывать каждое событие, которое меняет состояние приложения в базу данных. Таким образом получается что мы храним не состояние наших сущностей, а все события которые к ним относятся. Однако мы привыкли к тому чтобы манипулировать именно состоянием, оно храниться у нас в базе и мы всегда можем его посмотреть.
В случае с Event Sourcing мы тоже оперируем с состоянием сущности. Но в отличии от обычной модели мы это состоянием не храним, а воспроизводим каждый раз при обращении.

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

На самом же деле репозиторий не берет из базы готовое состояние агрегата UserAR (AR = Aggregate Root), он выбирает из базы все события которые ассоциируются с этим юзером, и потом воспроизводит их по порядку передавая в метод On() агрегата.

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

Из всего состояния агрегата мне нужна только _id юзера, так же я мог бы восстановить состояние пароля, имени и т.д. Однако эти поля могут быть модифицированы и другими событиями, не только User_CreatedEvent соответственно мне нужно будет обработать их все. Так как все события воспроизводятся по порядку, я уверен, что всегда работаю с последним актуальным состоянием агрегата, если я конечно написал обработчики On() для всех событий которые это состояние изменяют.

Давайте рассмотрим на примере создания пользователя как работает CQRS + Event Sourcing.

Создание и отправка команды

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

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

Затем мне нужен обработчик этой команды. Обработчику команды обязательно нужно передать ID нужного агрегата, по этому ID он получит агрегат из репозитория. Репозиторий строит объект агрегата следующим образом: берет из базы все события которые относятся к этому агрегату, создает новый пустой объект агрегата, и по порядку воспроизведет полученные события на объекте агрегата.
Но так как у нас команды создания — поднимать из базы нечего, значит создаем агрегат сами и передаем ему метаданные команды.

Посмотрим как выглядит конструктор агрегата.

Так же у агрегата обязательно должен быть конструктор без параметров, так как когда репозиторий воспроизводит состояние агрегата, ему надо сначала создать путой экземпляр, а затем передавать события в методы проекции (метод On(User_CreatedEvent created) является одним из методов проекции).
Немного уточню на счет проекции. Проекция — это воспроизведение состояния агрегата, на основе событий из Event Store, которые относятся к этом агрегату. В примере с пользователем — это все события для данного конкретного пользователя. А агрегате все те же события которые сохраняются через метод Apply, можно обработать во время воспроизведения его состояния. В нашем фреймворке для это достаточно написать метод On(/*EventType arg*/), где EventType — тип события которое вы хотите обработать.

Метод Apply агрегата инициирует отправку событий всем обработчикам. На самом деле события будут отправлены только при сохранение агрегата в репозиторий, а Apply просто добавляет их во внутренний список агрегата.
Вот обработчик события(!) создания пользователя, который записывает в read базу собственно самого пользователя.

У события может быть несколько обработчиков. Такая архитектура помогает сохранять целостность данных, если ваши данные сильно денормализированы. Допустим мне надо часто показывать общее количество пользователей. Но у меня их слишком много и операция count по всем очень затратна для моей БД. Тогда я могу написать еще один обработчик события, который будет уже относится к статистике и каждый раз при добавлении пользователя будет увеличивать общий счетчик пользователей на 1. И я буду уверен, что никто не создаст пользователя, не обновив при этом счетчик. Если бы я не использовал CQRS, а была бы у меня обычная ORM, пришлось бы следить в каждом месте где добавляется и удаляется пользовать чтоб обновился и счетчик.
А использование Event Sourcing’а даёт мне дополнительные приемущеста. Даже если я ошибся в каком-то EventHandler’е или не обработал событие везде где мне это надо, я могу легко это исправать просто перегенировав read базу с уже правильной бизнесс логикой.

С созданием понятно. Как происходит изменение агрегата и валидация команды? Рассмотрим пример со сменой пароля.
Я приведу только command handler и метод агрегата ChangePassword(), так как в остальных местах разница в общем не большая.

Command Handler
Aggregate Root

Хочу заметить что очень не желательно чтобы невалидное событие было передано в метод Apply(). Конечно вы сможете его обработать потом в event handler’е, но лучше вообще его не сохранять, если оно вам не важно, так как это только засорит Event Store.
В случае со сменой пароля вообще нет никакого смысла сохранять это событие, если вы конечно не собираете статистку по неудачным сменам пароля. И даже в этом случае следует хорошенько подумать, ножно ли это событие вам во write модели или есть смысл записать его в какое-нибудь темповое хранилище. Если вы предполагаете что бизнес логика валидации события может измениться то тогда сохраняйте его.

Вот собственно и все о чем я хотел рассказать в этой статье. Конечно она не раскрывает все аспекты и возможности CQRS + Event Sourcing, об этом я планирую рассказать в следующих статьях. Так же остались за кадром проблемы которые возникают при использовании данного подхода. И об этом мы тоже поговорим.
Если у вас есть какие-то вопросы, задавайте их в комментариях. С радостью на них отвечу. Так же если есть какие-то предложения по следующим статьям — очень хочется их услышать.

Источник

Строительный портал