2016-10-22 4 views
3

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

Одним из примеров размещения этой архитектуры является веб-api, который принимает команды на стороне записи и поддерживает запрос денормализованных представлений. Этот веб-api, вероятно, масштабируется для многих машин в ферме с балансировкой нагрузки.

Вопрос в том, где размещены обработчики событий модели чтения?

Возможные сценарии:

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

  2. Хостинг как часть веб-api. Если я использую EventStore, например, для хранения событий и обработки подписки на события, будет выполняться несколько обработчиков (по одному в каждом процессе веб-фермы) для каждого отдельного события и, таким образом, вызвать конфликты в обработчиках по мере их чтения/писать в свой магазин для чтения? Или мы гарантируем, что для данного агрегатного экземпляра все его события будут обрабатываться по очереди в порядке исполнения событий?

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

Может ли EventStore обрабатывать этот сценарий? Как другие обрабатывают обработку событий в согласованных архитектурах?

EDIT:

Чтобы уточнить, я говорю о процессе извлечения данных о событиях в Денормализованные таблицы, а не чтение этих таблиц для «Q» в CQRS.

Я предполагаю, что я ищу варианты того, как мы должны «внедрять и развертывать обработку событий для чтения моделей/sagas/etc, которые могут поддерживать избыточность и масштабирование, если, конечно, обрабатывается обработка событий в идемпотентном порядке.

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

автобус Event

Шина событие/очередь используется для публикации сообщений после того, как событие сохраняется, как правило, путем реализации хранилища. Заинтересованные стороны (подписчики), такие как модели чтения или саги/менеджеры процессов, используют «автобус/очередь» каким-то образом, чтобы обработать его идемпотентным способом.

Если очередь представляет собой pub/sub, это означает, что каждая нисходящая зависимость (read model, sagas и т. Д.) Может поддерживать только один процесс для подписки на очередь.Более одного процесса означают, что каждая обработка одного и того же события, а затем конкурирует, чтобы внести изменения вниз по течению. Идемпотентная обработка должна заботиться о проблемах согласованности/параллелизма.

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

Подписка/корм

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

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

+0

Хорошо q для группы ddd-cqrs-es slack или электронной почты –

+0

Когда вы говорите, прочитанная модель, вы хотите запросить денормализованные таблицы, где должно быть размещено приложение-запрос? – sagar

+0

Спасибо, sagar. Я добавил еще несколько вопросов к вопросу. – Mark

ответ

2

В нашем проекте мы используем на основе подписки прогнозов. Причины этого:

  • Совершения к записи стороне должен быть транзакционным и если вы используете две части инфраструктуры (магазин события и автобусные сообщения), вы должны начать использовать DTC или иначе вы рискуете свои события в сохраняться в магазине, но не публиковаться в автобусе или наоборот, в зависимости от вашей реализации. DTC и двухфазные коммиты - это неприятные вещи, и вы не хотите идти этим путем.
  • События обычно публикуются в шине сообщений в любом случае (мы делаем это также с помощью подписки) для обмена событиями между различными ограниченными контекстами. Если вы используете подписчиков сообщений для обновления вашей модели чтения, когда вы решите перестроить модель чтения, ваши другие подписчики также получат эти сообщения, и это приведет к недействительности системы. Я думаю, вы уже об этом подумали, говоря, что у вас должен быть только один подписчик для каждого опубликованного типа сообщения.
  • У потребителей сообщений сообщений нет гарантии на порядок сообщений, и это может привести к бесполезности модели чтения.
  • Потребители сообщений обычно обрабатывают повторные попытки, отправляя сообщение обратно в очередь и обычно к концу очереди для повторной попытки. Это означает, что ваши события могут сильно нарушаться. Кроме того, обычно после некоторого количества попыток, потребитель сообщения отказывается от ядовитого сообщения и помещает его в некоторый DLQ. Если это будет ваша проекция, это будет означать, что одно обновление будет игнорироваться, в то время как другие будут обработаны. Это означает, что ваша прочитанная модель будет находиться в противоречивом (недействительном) состоянии.

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

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

У нас есть два отдельных - один для проецирования на модель чтения и еще один для публикации событий на шине сообщений. Эта конструкция доказала свою эффективность.

+0

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

+0

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

+0

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

0

В частности, для EventStore у них теперь есть competing consumers, которые являются подписками на основе сервера, где многие клиенты могут подписаться на группу подписки, но только один клиент получает это сообщение.

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

+0

Конкурирующие потребители не очень привлекательны для модели прочитанной модели из-за отсутствия гарантии заказа. Вы должны очень осторожно отказаться от заказа события при построении модели чтения. –

+0

Да, хороший момент, порядок важен при проецировании событий на чтение моделей. – Matt

Смежные вопросы