CQRS - применение команды на основе решения из нескольких проекций - PullRequest
0 голосов
/ 27 октября 2019

Вопрос связан с CQRS - у меня есть пользователь, который хочет заказать что-то из Интернета и представлен с графическим интерфейсом, показывающим его баланс = 100 $ и запас = 1 товар. Допустим, у нас есть 2 сервиса AccountService и StockService с отдельными проблемами. Чтобы сгенерировать GUI для клиентской третьей службы, AggregatorService прослушивает события домена из AccountService и StockService, проецирует представление и создает GUI для клиентов.

Когда пользователь решает заказать этот элемент, он щелкаеткнопка и команда для заказа отправляются на AccountService. Здесь мы загружаем AccountAggregate, чтобы уменьшить баланс цены товара, который необходимо заказать. Но прежде чем я смогу это сделать, я должен проверить, доступен ли предмет (или как-то зарезервировать его). Единственное, что приходит мне в голову:

  • Считать текущий запас элемента из модели чтения StockService, но может случиться так, что модель чтения других служб обновляется через секунду после того, как япрочитайте его (например, кто-то купил предмет, поэтому фактический запас = 0, но прочитайте модель по-прежнему = 1).
  • Прежде чем уменьшать баланс, вызовите какой-нибудь метод на StockService, чтобы зарезервировать предмет на некоторое время. Если заказ не был успешным (например, недостаточно средств на балансе, я должен был бы как-то отменить его резервирование). Это должен быть какой-то вызов sync-REST, и он, вероятно, медленнее, чем какое-либо асинхронное решение (если оно есть).

Есть ли рекомендации для этого варианта использования?

1 Ответ

1 голос
/ 28 октября 2019

У вас есть 2 варианта, в зависимости от того, принимаете ли вы возможную последовательность или нет.

Используя немедленную согласованность, у меня будет OrderService, который получает запрос заказа и выполняет асинхронные вызовы AccountService.ReservePayment () и StockService.ReserveStock (). В случае сбоя любого из них вы вызываете AccountService.UndoReservePayment () и StockService.UndoReserveStock (). Если оба успешны, Вы вызываете AccountService.CompleteReservePayment () и StockService.CompleteReserveStock (). Убедитесь, что у каждой резервной копии есть временная метка, чтобы процесс демона мог иногда запускаться и отменять любые резервы, которые старше часа или около того.

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

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

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

Цена, которую вы платите здесь, заключается в том, что пользователь может получить сообщение об успехе во времяотправьте заказ, а через несколько минут получите электронное письмо с сообщением о том, что распродажа не состоялась, потому что на складе нет. Это то, что делают многие крупные ритейлеры, в том числе Amazon, Target, Walmart и т. Д. Конечная согласованность может иметь большое значение для облегчения загрузки и стоимости серверной части.

...