Я работаю над небольшим примером приложения, чтобы изучить концепции 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.