Как должны размещаться обработчики событий источника событий для построения модели чтения?

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

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

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

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

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

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

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

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

EDIT:

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

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

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

автобусная событие

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

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

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

подписка/питания

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

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

2 ответов


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

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

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

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

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


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

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