Подтвердить что ты не робот

События CQRS не содержат сведений, необходимых для обновления модели чтения

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

К сожалению, это довольно распространенный сценарий.

Пример. Я добавляю пользователя в группу, поэтому я отправляю команду addUserToGroup (userId, groupId). Это получено, обрабатывается обработчиком команд, создается событие "userAddedToGroup", сохраняется и публикуется.

Теперь обработчик события получает это событие и оба идентификатора. Теперь должно быть представление, в котором перечислены все пользователи с именами групп, в которых они находятся. Чтобы обновить модель чтения для этого представления, нам нужен идентификатор пользователя (который у нас есть) и имя группы (которое мы надеваем, t есть, у нас есть только его id).

Итак, вопрос: как мне обрабатывать этот сценарий?

В настоящее время мне приходят четыре варианта, все с их конкретными недостатками:

  • Прочитанная модель запрашивает домен. = > Запрещено и даже не возможно, поскольку у домена только поведение, нет (общедоступное) состояние.

  • Модель чтения считывает имя группы из другой таблицы в модели чтения. = > Работает, но что, если нет соответствующей таблицы?

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

  • Не обрабатывайте событие с помощью "обычного" обработчика событий, а запускайте процесс ETL в фоновом режиме, который связан с хранилищем событий, создает необходимые данные и записывает модель чтения. = > Работает, но для меня это кажется немного слишком сложным для такого простого сценария.

Итак, возникает вопрос: как я правильно справляюсь с этим сценарием?

4b9b3361

Ответ 1

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

SubmitPurchaseOrder
    Shopping Cart Id
    Shipping Address
    Billing Address

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

PurchaseOrderSubmitted
    Items (Id, Name, Amount, Price)
    Shipping Address
    Shipping Provider
    Our Shipping Cost
    Shipping Cost billed to Customer
    Billing Address
    VAT %
    VAT Amount
    First Time Customer
    ...

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

Кроме того, событие может быть обогащено путем запроса модели чтения или даже другого BC (например, для получения фактического НДС% в зависимости от состояния) во время обработки.

Вы правильно предполагаете, что события могут (и, вероятно, будут) меняться со временем. Это вообще не имеет значения, если вы используете управление версиями: добавьте новое событие (например, SubmitPurchaseOrderV2) и добавьте соответствующий обработчик событий ко всем классам, которые должны его использовать. Не нужно менять старое событие, оно все равно может быть использовано, поскольку вы не модифицируете интерфейс, вы его расширяете. Это в основном сводится к очень хорошему примеру открытого/закрытого принципа на практике.

Ответ 2

Существует два общих решения.

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

2) Проектировать несколько потоков одновременно. Напишите проектор, который следит за информацией из разных потоков и объединяет их вместе. Вы можете наблюдать за событиями пользователя и группы, а также с вашим пользователем, добавленным в групповое событие. В зависимости от порядка событий в вашей системе вы можете знать, что пользователь находится в группе, прежде чем вы узнаете имя группы, но перед тем, как начать, вы должны знать общие свойства вашего хранилища событий.

Ответ 3

Вариант 2 будет в порядке, ваш вопрос о том, "как насчет несоответствия в таблице имен модели имен групп", не будет применяться. никакие данные не должны быть удалены, должны быть аннулированы, когда предыдущее событие (например, группа удаления) было заменено. В конце концов строка в таблице групп работает эффективно, и вы можете без проблем читать имя группы. Единственной очевидной проблемой может быть несогласованность в скорости, но это еще одна проблема, события должны обрабатываться упорядоченно независимо от скорости их обработки.