Я работаю над бэкэндом и пытаюсь реализовать шаблоны CQRS. Я довольно ясно разбираюсь в событиях, но иногда борюсь с командами.
Я видел, что команды запрашиваются пользователями, например ChangePasswordCommand. Однако на уровне реализации пользователь просто вызывает конечную точку, обрабатываемую некоторым контроллером.
Я могу внедрить UserService в свой контроллер, который будет обрабатывать логику домена, и именно так работают базовые учебные пособия (я использую Nest.js). Однако я чувствую, что, возможно, именно здесь я должен использовать команду - так что я должен выполнить команду ChangePasswordCommand в своем контроллере, а затем ее обработает модуль домена?
Важно то, что мне нужно возвращаемое значение от команды, что не является проблемой с точки зрения реализации, но это выглядит не очень хорошо с точки зрения CQRS - я должен ДОБАВИТЬ и ПОЛУЧИТЬ одновременно.
Или, может быть, последний вариант — выполнить команду в контроллере, а затем сгенерировать событие (PasswordChangedEvent) в обработчике команд. Затем дождитесь возврата события и верните значение в контроллер.
Последний вариант мне кажется неплохим, но у меня проблемы с четкой реализацией внутри жизненного цикла запроса.
я основываюсь на https://docs.nestjs.com/recipes/cqrs





По мере развития вашей архитектуры вы можете обнаружить, что вам требуется командная шина, если вы используете Processes/Sagas для управления рабочими процессами и межагрегатной связью. Если и когда это так, естественно, имеет смысл использовать эту шину для всех команд.
Ниже приведен метод, который я бы предпочел:
execute the command in controller and then emit an event (PasswordChangedEvent) in command handler. Next, wait till event comes back and return the value in controller.
Что касается деталей реализации, то в .NET мы используем службу веб-сокетов SignalR, которая считывает шину событий (где публикуются все события) и пересылает события клиентам, подписавшимся на них.
В этом случае рабочий процесс будет таким:
@ŁukaszOstrowski Есть много факторов, которые следует учитывать при сдаче CQRS. Я не знаю, является ли быстрое изменение бизнес-логики одним из преимуществ использования этого шаблона. Вы можете применить DDD в стандартной n-уровневой архитектуре и отказаться от накладных расходов CQRS. Что касается REST, вы обычно возвращаете 201 Accepted при отправке команды. Затем ваш клиент дождется завершения команды, прежде чем продолжить. Это могут быть веб-сокеты или опрос на стороне клиента. Или вы могли бы также обработать команду от вашего контроллера и вернуть результат клиенту. Каждый подход имеет свои компромиссы.
Хорошо, спасибо, я подумаю об этом. Идея в том, что я строю стартап и мне нужно быстро прототипировать, но с другой стороны — все может часто меняться. Внедряя CQRS, я вижу преимущества разделения своих доменов
Занимаясь программным бизнесом в течение нескольких лет (используя CQRS+ES) и недавно реализовав свою первую идею «стартапа» (используя стандартный N-уровень и без причудливости), я могу с уверенностью сказать, что вы должны решить, на чем вы сосредоточены. стартап или ориентированы на собственный технологический рост. Любой из них является отличным мотиватором для того, чтобы идти по пути, которым вы идете, но на самом деле это два конкурирующих приоритета. Выбирайте то, что имеет значение, и будьте счастливы. Не выбирайте оба.
Я согласен. Чего я пытаюсь добиться, так это дополнительных накладных расходов на архитектуру, поэтому я могу отказаться от большего количества реализаций. Архитектура, управляемая событиями, для меня в новинку (в любом случае я обычно не использую бэкенд), но я уже вижу ее преимущества. Никаких причудливых вещей, я не использую источники событий, просто мне нужен более декларативный подход, который я использую во внешних приложениях с Redux Saga. Я далек от инженерии, просто хочу построить стабильный фундамент.
Хотя отвечать от @cperson технически верен, я хотел бы добавить к нему несколько нюансов.
Во-первых, то, что может быть неясно из описания ответа, где он советует "генерировать событие (PasswordChangedEvent) в обработчике команд". Это то, что я бы тоже предпочел, но будьте осторожны:
Command является частью уровня инфраструктуры, а Event является частью домена.AggregateRoot, который генерирует событие.mergeObjectContext или eventBus.publish (см. NestJS документы).Еще один момент, на который я хотел обратить внимание, заключается в том, что предполагается архитектура, основанная на событиях, то есть применение CQRS/ES. Хотя CQRS часто используется в сочетании с Event Sourcing, ничто не предписывает это делать. Event Sourcing может дать дополнительные преимущества, но также сопряжен со значительной дополнительной сложностью. Вы должны тщательно взвесить все за и против наличия ЭС.
Во многих случаях вам не нужен Event Sourcing. Наличие только CQRS уже дает вам много преимуществ, таких как наличие вашего домена / ограниченных контекстов. Разделение между чтением и записью, команды с одной ответственностью + запросы (в целом более SOLID), более чистая архитектура и т. д. На более высоком уровне легче сместить фокус с «как мне это реализовать (с точки зрения CRUD)?» на 'как эти пользовательские требования вписываются в модель предметной области?'.
Без ES вы можете иметь одну реляционную базу данных и, например. сохраняться с использованием TypeORM. Вы можете сохранять события, но это не нужно. Во многих сценариях вы можете избежать возможной согласованности, когда клиентам необходимо подписываться на события (возможно, вы просто используете их для управления сагами и обновления представлений/проекций на стороне чтения).
Вы всегда можете начать только с CQRS и добавить Event Sourcing позже, когда возникнет необходимость.
Спасибо за ответ. Я полностью увлекаюсь архитектурой, управляемой событиями (разработка приложения начинается с действий, связь через события), и я реализую ее с помощью cqrs, но я не увлекаюсь самой ES. Я согласен, что это отличная концепция (я использую ее на интерфейсе с редуксом), но сложность на сервере слишком высока для моих нужд.
Спасибо за ответ. Так что вы в принципе подтвердили мои мысли. Проблема, с которой я столкнулся при таком подходе, заключается в том, что я использую имеют для настройки реализации, а также того, как я взаимодействую между клиентом и сервером, чтобы соответствовать этому шаблону. Я надеялся создать свой бэкэнд/API с использованием CQRS, чтобы он был открыт для быстрых изменений бизнес-логики. Но чтобы добиться этой приятной развязки, теперь мне нужно отказаться от стандартного интерфейса REST и реализовать веб-сокеты — это крутая технология, но для меня это может быть излишним. Я хотел бы каким-то образом инкапсулировать шаблоны команд/событий между запросом и циклом ответа. Есть идеи?