Event Sourcing и CQRS, что я пропустил? - PullRequest
1 голос
/ 04 марта 2020

Я начал читать о шаблоне Event-Sourcing в сочетании с CQRS. Насколько я понимаю, шаблон CQRS - это шаблон, в котором мы разделяем действия записи и чтения. Источник событий - это шаблон, в котором все в системе инициируется командой, которая инициирует событие. Для шаблона поиска событий требуется шина событий. Есть пара вещей, которые мне не удалось понять.

Хранилище событий содержит все события, которые произошли с определенной сущностью. Если я хочу запросить текущее состояние этой сущности, мне нужно запросить все события, которые произошли с этой сущностью, и воссоздать ее текущее состояние. Вся история событий присутствует в магазине событий. Почему у меня не может быть микросервиса, который отвечает за сохранение каждого события в базе данных событий (если я хочу записать эти события для дальнейших действий. Что-то вроде kafka), и отдельный микросервис, который обновляет изменения в сущности в обычном база данных (например, простое обновление документа объекта в mongodb). Когда эти микросервисы завершат свою работу, это событие будет удалено из хранилища событий (скажем, я реализую это хранилище событий, используя очередь). Таким образом, когда мне нужно запросить текущее состояние объекта, я просто запрашиваю базу данных вместо того, чтобы запрашивать хранилище событий и перестраивать текущее состояние (или пересчитывать состояние на основе хранилища событий и периодически кэшировать результат). Я не понимаю, почему обязательно хранить все события навсегда, почему это не обязательно?

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

Ответы [ 4 ]

3 голосов
/ 05 марта 2020

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

Шаблон, на который вы ссылались (чтение всех событий для восстановления состояния объекта), - это то, что я называю "Домен «Управляемый дизайн» в Event Sourcing.

Ваши мысли больше связаны с подходом, ориентированным на «событие + состояние».

Давайте посмотрим ближе на оба эти метода.

DDD и совокупные потоки

Одним из тактических паттернов DDD является Агрегатный паттерн. Это в основном граница согласованности. Команда может быть применена только к одному агрегатному экземпляру и поэтому формирует транзакцию. Когда команда обрабатывается, совокупное состояние изменяется, и, следовательно, создается новое событие домена (или несколько событий). Затем мы сохраняем событие (я) в хранилище событий как одну транзакцию. Все события для одного объекта хранятся в одном потоке, который мы обычно называем «агрегатным потоком», а имя потока обычно формируется из типа агрегата и его идентификатора (например, Order-123).

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

Я не уверен, что вы имели в виду при упоминании "запроса состояния объекта". Если вы имеете в виду «получение состояния сущности по id» - это кажется правильным. Для запросов вы этого не делаете. Вот где CQRS играет свою роль. Вы проецируете необходимые события в другое место, базу данных, которая позволяет выполнять запросы. В этой базе данных у вас есть прогнозируемое состояние вашей сущности. Нет ограничений в том, что проекция использует только события одного типа сущности, на самом деле это скорее анти-паттерн. Модели чтения (прогнозируемое состояние) используются для определенных c целей, часто обусловленных потребностями пользователей (своего рода пользовательский интерфейс).

События + состояние

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

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

Что касается других вещей, я считаю, что другие ответы уже охватывают эти вопросы.

3 голосов
/ 05 марта 2020

Для шаблона поиска событий требуется шина событий.

Шина не требуется для поиска событий, если только вам не нужно уведомлять другие системы / домены об изменении (событии).

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

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

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

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

Вся история событий присутствует в хранилище событий. Почему у меня не может быть микросервиса, который отвечает за сохранение каждого события в базе данных событий (если я хочу записать эти события для дальнейших действий. Что-то вроде kafka), и отдельный микросервис, который обновляет изменения в сущности в обычном базы данных (например, простое обновление документа объекта в mongodb).

Вы можете!

Когда эти микросервисы завершат свою работу, это событие будет удалено из события- store (допустим, я реализую это хранилище событий, используя очередь).

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

Когда вы делаете CQRS, и особенно когда вы делаете Event Sourcing, вам нужно изменить свою ментальную модель того, что означает «текущее состояние». Фактическая истина хранится где-то (хранилище событий, реляционная база данных и т. Д. c.), И когда вы запрашиваете, вы проецируете эту истину в любой необходимый вам формат.

Например, у меня есть база данных пользователи, которые хранят FirstName в одном столбце и LastName в другом. У строки, которая представляет меня, есть «Фил» в столбце «FirstName» и «Sandler» в столбце «LastName». Когда я показываю данные в пользовательском интерфейсе, я отображаю их как «Сандлер, Фил». Почему бы просто не сохранить его в базе данных документов под именем «Sandler, Phil» и покончить с этим? Потому что, нормализуя данные, я точно записал правду и имею возможность проецировать данные по-разному в будущем, если возникнет такая необходимость.

Как и текущее состояние в приведенном выше примере, данные хранятся в двух столбцах или это "Сэндлер, Фил"? В CQRS вы должны думать не об этом с точки зрения текущего состояния, а с точки зрения ваших двух отдельных моделей: правды (сторона записи) и того, как она проецируется (сторона чтения).

2 голосов
/ 04 марта 2020

Я не понимаю, почему необходимо хранить все события навсегда, почему это не обязательно?

За Мартин Фаулер , (выделение мой)

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

Это приводит к ряду средств, которые могут быть построены поверх журнала событий:

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

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

2 голосов
/ 04 марта 2020

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

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

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

Почему события не хранятся в чем-то вроде Кафка, а НЕ использовать их при загрузке действительно событийный сорсинг? Если вы не сохраняете снимки в том же БД, что и события, то вы рискуете столкнуться с реальными рисками возникновения конфликтов параллелизма: например, двукратным, конфликтующими событиями ИЛИ пропущенными событиями, если вы решите использовать самое большее - однажды семантика на поднятие событий. Это прямо означает, что вы не можете полагаться на события, которые вы излучаете, когда-либо являющиеся источником истины.

...