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

14

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

Вот некоторый псевдокод, чтобы показать реализацию

Basket { BasketId; OrderLines; Address; }

// basket events
BasketCreated { BasketId; }
ItemAdded { BasketId; ProductId; Quantity }
AddItemSucceeded { BasketId; ProductId; Quantity }
AddItemRevoked { BasketId; ProductId; Quantity }
ItemRemoved { BasketId; ProductId; Quantity }
CheckedOut { BasketId; Address }

Product { ProductId; Name; Price; }

// product events
ProductReserved { ProductId; Quantity }
ProductReservationFailed { ProductId; Quantity }
ProductReservationCancelled { ProductId; Quantity; }

Команды очень похожи на события, используя императивное имя, а не прошедшее время.

Прямо сейчас они прекрасно работают независимо друг от друга. Я запускаю команду AddItem, и она создает ItemAddedсобытие в Basketагрегате, которое делает то, что ему нужно, с состоянием «корзины». Аналогично, для продукта команда и события работают просто отлично.

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

Менеджер процесса будет делать следующее:

on BasketCreated: CreateShoppingProcess
on ItemAdded: ReserveProduct
on ProductReserved: SucceedAddingItem // does nothing, but needs to be there so that the basket knows it can check out
on ProductReservationFailed: RevokeAddItem
on RemoveItem: CancelProductReservation
on Checkout: CreateOrder // create an order and so on...

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

  1. Нужно ли сохранять диспетчер процессов? Кажется, что я делаю, но я не уверен
  2. Если я это сделаю, мне нужно сохранить события для диспетчера процессов. Однако события, которые Он слушает, связаны с совокупностями. Должен ли я добавить идентификатор процесса к ним? У меня есть отдельные события только для менеджера процесса? Как это сделать и сохранить как можно более сухим
  3. Как мне узнать, для какой корзины предназначены ProductReservedсобытия? Это нормально, чтобы иметь BasketIdна них тоже, или это утечка информации?
  4. Как мне сохранить связь между событиями, как мне узнать, что ItemAddedпроизвело какое ProductReservedсобытие? Я передаю EventId? Это кажется странным ...
  5. Должен ли я реализовать Basketкак менеджер процессов, а не простой агрегат?

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

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

Иван Пинтар
источник
Кажется, вы пытаетесь закодировать в своей системе формальный процесс, который перефразирует существующий неформальный вариант использования, состоящий из ряда команд. При этом создается впечатление, что вы создаете множество избыточных команд и событий помимо существующих. Это твое намерение? Что нужно бизнесу для формализации вещей как процесса в коде? Что в вашем домене требует от вас идентифицировать этот процесс и обосновать его как полноценную концепцию?
guillaume31
Это полностью составленный проект, целью которого является научиться тому, как заставить два относительно независимых агрегата работать вместе. Так что на самом деле нет реальной «бизнес-потребности», и я стараюсь максимально избегать избыточности в этих командах и событиях. Отсюда путаница с менеджером процессов., Потому что кажется, что это не должно повторять вещи, которые агрегаты уже обрабатывают. Однако мне нужно как-то поддерживать связь между этими двумя агрегатами. Кажется, что использование причинно-следственной связи может помочь, но я должен попробовать это.
Иван Пинтар

Ответы:

14

Посмотрите, что Ринат Абдуллин написал о развитии бизнес-процессов . В частности, обратите внимание на его рекомендации по разработке бизнес-процессов в быстро меняющейся среде - менеджер процессов является «просто» автоматической заменой человека, смотрящего на экран.

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

Нужно ли сохранять диспетчер процессов? Кажется, что я делаю, но я не уверен

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

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

Нет - менеджер процесса является менеджером . Он не делает ничего полезного сам по себе; вместо этого он указывает агрегатам выполнять работу (т. е. вносить изменения в книгу рекордов).

Как мне узнать, для какой корзины предназначены события ProductReserved? Можно ли использовать BasketId на них, или это утечка информации?

Конечно. Примечание: в большинстве «реальных» торговых доменов вы не будете настаивать на резервировании товара до обработки заказа; это добавляет ненужные раздоры в бизнес. Скорее всего, ваш бизнес захочет принять заказ, а затем извиниться в редком случае, если заказ не может быть выполнен в нужное время.

Как сохранить связь между событиями, как мне узнать, какой ItemAdded вызвал какое событие ProductReserved?

Сообщения имеют метаданные - в частности, вы можете включить causationIdentifier (чтобы вы могли определить, какие команды произвели какие события) и correlationIdentifier для общего отслеживания диалога.

Например, менеджер процесса записывает свой собственный идентификатор в качестве correlationId в команде; события, сгенерированные копией идентификатора корреляции команды, и ваш менеджер процессов подписывается на все события, которые имеют собственный корреляционный идентификатор.

Должен ли я реализовать Корзину как менеджер процессов вместо простого агрегата?

Моя рекомендация - нет. Но Уди Дахан придерживается другого мнения, которое вы должны пересмотреть; а именно то, что CQRS имеет смысл, только если ваши агрегаты - саги - Уди использовал сагу в том месте, где менеджер процессов стал доминирующим.

должны ли менеджеры процессов получать агрегаты?

На самом деле, нет? Менеджеры процессов в основном занимаются оркестровкой, а не состоянием домена. Экземпляр процесса будет иметь «состояние» в форме истории всех событий, которые он наблюдал, - правильное действие в ответ на событие Z зависит от того, видели ли мы события X и Y или нет. Таким образом, вам может потребоваться иметь возможность хранить и загружать представление этого состояния (которое может быть чем-то плоским или может быть историей наблюдаемых событий).

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

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

Не совсем - государственное управление - это не делатель, а средство отслеживания. В обстоятельствах, когда менеджер процесса не должен испускать никаких сигналов, вы даете ему инертные связи с миром. Другими словами, dispatch(command)это неоперация.

VoiceOfUnreason
источник
1
Вы говорите: вы можете перестраивать менеджер процессов из истории событий каждый раз, когда вам это нужно ... Но чтобы перестроить его, мне нужно сохранить для него события. Или я должен восстановить его из событий в совокупностях? Часть, с которой я борюсь: с агрегатами, события имеют агрегатный идентификатор, и его легко перестроить, найдя все события с этим агрегатным идентификатором. Но как мне это сделать для менеджера процессов? Должен ли я делать это для менеджера процесса? Или менеджер процессов должен искать агрегаты, когда ему нужно что-то решить на основе наступившего события?
Иван Пинтар
Чего мне не хватало, так это понятия причинности и корреляции в источниках событий. Как только я посмотрел на это, ваш ответ на четвертый вопрос, наконец, имел смысл.
Иван Пинтар
1
Я хотел бы получить ответ на первый комментарий @IvanPintar; должны ли менеджеры процессов получать агрегаты? Должны ли они строить свои собственные на основе событий, которые он обрабатывает? В этом случае его обработчики событий должны быть свободны от побочных эффектов, верно?
Джефф
@Jeff В одном примере, который я сделал, у менеджера процессов было свое собственное хранилище, которое обновлялось с каждым обработанным событием, что-то вроде модели чтения. Это было легко сделать, и было легко отследить, что он уже обработал. В другом примере менеджер процессов создал и сохранил свои собственные события, построенные из совокупных событий. Аналогично вышеописанному, но состояние было получено из событий. В зависимости от сложности состояния, которое поддерживает диспетчер процессов, может быть проще сделать одно или другое. Я нашел первый подход проще.
Иван Пинтар
Интересно, так что это более или менее зависит от разработчика, если менеджер процессов отвечает на события и отправляет команды в конце?
Джефф
2

То, что вы ищете, имеет шаблон под названием «Saga», который по сути является менеджером процессов.

Сага также идеально подходит для длительных процессов, потому что она может поддерживать состояние между коррелированными командами.

Вот отличная статья о саги

Bishoy
источник