Как управлять изменениями ViewModel в архитектуре CQRS Event Sourcing

#design-patterns #architecture #cqrs #event-sourcing

#шаблоны проектирования #архитектура #cqrs #поиск событий

Вопрос:

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

1) Что произойдет, если после того, как приложение некоторое время было запущено, появится новое требование добавить дополнительное поле в ViewModel в базе данных ReadModel? Допустим, почтовый индекс клиента требуется в CustomerList ViewModel, где его ранее не было. Итак, дополнительный столбец можно легко добавить в базу данных ViewModel, но как он заполняется? Насколько я вижу, единственный способ — очистить базу данных чтения и воспроизвести все события с нуля, чтобы создать резервную копию базы данных ReadModel. Но что, если приложение запущено в течение месяцев или лет (как мы надеемся, так и будет). Это могут быть миллионы событий, которые нужно воспроизвести, просто чтобы добавить данные для столбца zipcode.

У меня такая же проблема, если по какой-либо технической причине база данных ReadModel не синхронизирована, или мы хотим добавить новую базу данных ReadModel. Кажется, что чем старше приложение и чем чаще оно используется, тем сложнее и дороже восстановить актуальную readmodel. Или я где-то упускаю какой-то трюк? Что-то вроде моментальных снимков ReadModel?

2) Что произойдет, если после воспроизведения всех миллионов событий для создания резервной копии прочитанной базы данных некоторые данные не совпадут с ожидаемыми (т. Е. Они выглядят неправильно). Считается, что, возможно, причиной этого могла быть ошибка где-то в процедурах хранения событий или денормализации (и кажется, что если есть что-то, на что вы можете положиться при написании кода, так это ошибки). Как приступить к отладке этого! Это кажется невыполнимой задачей. Или, может быть, опять же, я упускаю какой-то трюк.

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

Спасибо за любое время и вклад.

Ответ №1:

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

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

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

Комментарии:

1. Спасибо за ваш ответ. Ваши взгляды на то, что время воспроизведения события не является проблемой (даже для миллионов событий), вселяют уверенность. Кстати, мне нравится ваш блог, спасибо, что поделились!

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

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

4. «Прелесть использования поиска событий с CQRS заключается в возможности уничтожить модель чтения и перестроить ее с нуля, как уже упоминалось». Странная причина для принятия стиля CQRS — возможность уничтожения базы данных…

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

Ответ №2:

Я несколько новичок в CQRS, поэтому это может быть не самый рекомендуемый маршрут (но iirc я выбрал его из одного из списков рассылки CQRS / DDDD).

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

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

Комментарии:

1. Да, инженер, который запустил в производство пару архитектур ES / CQRS, сказал мне, что раньше изменения модели данных рассматривались как обычные события и пытались управлять ими наилучшим образом. Это особенно сложно, когда стороне чтения приходится больше заниматься отменой атрибутов, чем добавлением, потому что в этом случае вам нужно просто обновить свой код (при строго типизированном) и предоставить значения по умолчанию

Ответ №3:

У меня еще нет готового к производству приложения, использующего cqrs с event sourcing, поэтому вот только мой опыт попытки его создания.

1) Read Model rebuild . Да, вам в основном приходится перестраивать всю базу данных модели чтения, как только что-то в ней меняется. И если событий много, это может занять много времени. Таким образом, перестройка модели чтения должна быть высоко оптимизирована (использовать пакетирование событий и т.д.). Я считаю, что поиск событий лучше всего подходит в случаях, когда существует высокое соотношение чтения и записи. Поэтому для некоторых чрезвычайно изменчивых данных может быть разумно не сохранять их как события домена. Но тогда вопрос о емкости хранилища также не так уж далек. В любом случае, вы можете применить cqrs только к той части системы, где она подходит лучше всего (например, я, вероятно, не стал бы сохранять графическое изображение как часть события).

2) Debugging . Крайне маловероятно, что в хранении событий произошла ошибка (это должно беспокоить фреймворк), и всегда легко проверить, какие события находятся в хранилище. Что касается команды для создания ожидаемых событий, у вас должны быть тесты здесь, и эти тесты, вероятно, будут самыми ценными тестами в системе. Для денормализаторов у вас также могут быть тесты, но я бы не стал утруждать себя написанием тестов для тривиальных денормализаторов, если их корректность видна невооруженным глазом. При этом я несколько раз использовал debugger для поиска проблем в некоторых более сложных денормализаторах; было не так уж весело пытаться определить, из-за какого события что-то пошло не так.

Ответ №4:

В вашей модели также можно добавить событие объединения. Это может быть запущено как произвольная задача после получения X количества событий (скажем, 500)

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