Я работаю над небольшим примером приложения, чтобы изучить концепции 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...
Вопросы, на которые я не смог найти однозначных ответов:
- Нужно ли сохранять диспетчер процессов? Кажется, что я делаю, но я не уверен
- Если я это сделаю, мне нужно сохранить события для диспетчера процессов. Однако события, которые Он слушает, связаны с совокупностями. Должен ли я добавить идентификатор процесса к ним? У меня есть отдельные события только для менеджера процесса? Как это сделать и сохранить как можно более сухим
- Как мне узнать, для какой корзины предназначены
ProductReserved
события? Это нормально, чтобы иметьBasketId
на них тоже, или это утечка информации? - Как мне сохранить связь между событиями, как мне узнать, что
ItemAdded
произвело какоеProductReserved
событие? Я передаюEventId
? Это кажется странным ... - Должен ли я реализовать
Basket
как менеджер процессов, а не простой агрегат?
После еще одного исследования я пришел к следующему: Сага - это то, что хранит свои собственные события и слушает события извне. По сути, это Агрегат, который также может реагировать на события, происходящие за пределами его собственного маленького мира.
Диспетчер процессов работает с событиями извне и отправляет команды. Его история может быть перестроена из событий, которые произошли в агрегатах, которые имеют общий идентификатор, такой как correlationId.
источник
Ответы:
Посмотрите, что Ринат Абдуллин написал о развитии бизнес-процессов . В частности, обратите внимание на его рекомендации по разработке бизнес-процессов в быстро меняющейся среде - менеджер процессов является «просто» автоматической заменой человека, смотрящего на экран.
Моя собственная ментальная модель менеджера процессов заключается в том, что это проекция, основанная на событиях, которую вы можете запросить для получения списка ожидающих команд.
Это модель для чтения. Вы можете перестраивать менеджер процессов из истории событий каждый раз, когда вам это нужно; или вы можете рассматривать это как снимок, который вы обновляете.
Нет - менеджер процесса является менеджером . Он не делает ничего полезного сам по себе; вместо этого он указывает агрегатам выполнять работу (т. е. вносить изменения в книгу рекордов).
Конечно. Примечание: в большинстве «реальных» торговых доменов вы не будете настаивать на резервировании товара до обработки заказа; это добавляет ненужные раздоры в бизнес. Скорее всего, ваш бизнес захочет принять заказ, а затем извиниться в редком случае, если заказ не может быть выполнен в нужное время.
Сообщения имеют метаданные - в частности, вы можете включить causationIdentifier (чтобы вы могли определить, какие команды произвели какие события) и correlationIdentifier для общего отслеживания диалога.
Например, менеджер процесса записывает свой собственный идентификатор в качестве correlationId в команде; события, сгенерированные копией идентификатора корреляции команды, и ваш менеджер процессов подписывается на все события, которые имеют собственный корреляционный идентификатор.
Моя рекомендация - нет. Но Уди Дахан придерживается другого мнения, которое вы должны пересмотреть; а именно то, что CQRS имеет смысл, только если ваши агрегаты - саги - Уди использовал сагу в том месте, где менеджер процессов стал доминирующим.
На самом деле, нет? Менеджеры процессов в основном занимаются оркестровкой, а не состоянием домена. Экземпляр процесса будет иметь «состояние» в форме истории всех событий, которые он наблюдал, - правильное действие в ответ на событие Z зависит от того, видели ли мы события X и Y или нет. Таким образом, вам может потребоваться иметь возможность хранить и загружать представление этого состояния (которое может быть чем-то плоским или может быть историей наблюдаемых событий).
(Я говорю «не совсем», потому что агрегат определен достаточно расплывчато, так что не совсем неправильно утверждать, что список наблюдаемых событий является «агрегатом». Различия более семантические, чем реализация - мы загружаем состояние процесса и затем решаем, какие сообщения следует отправить в части системы, отвечающие за состояние домена . Здесь немного махают рукой.)
Не совсем - государственное управление - это не делатель, а средство отслеживания. В обстоятельствах, когда менеджер процесса не должен испускать никаких сигналов, вы даете ему инертные связи с миром. Другими словами,
dispatch(command)
это неоперация.источник
То, что вы ищете, имеет шаблон под названием «Saga», который по сути является менеджером процессов.
Сага также идеально подходит для длительных процессов, потому что она может поддерживать состояние между коррелированными командами.
Вот отличная статья о саги
источник