Как обрабатывать ошибки после проверки в команде (DDD + CQRS)

18

Например, когда вы отправляете форму регистрации, вы должны подтвердить Domain Model( WriteModelв CQRS), что она находится в действительном состоянии (например, синтаксис адреса электронной почты, возраст и т. Д.).

Затем вы создаете Commandи отправляете его Command Bus.

Я понимаю, что Команды не должны ничего возвращать.

Итак, как вы справляетесь с ошибкой Command Bus? (Например, пользователь зарегистрировался за 1 секунду до того же username/email).

Откуда вы знаете, что команда завершилась неудачно, и как вы узнали об ошибке?

JorgeeFG
источник
2
Вам не нужен автобус для мероприятий. Почему все думают, что вам нужна шина событий при реализации CQRS, я просто не знаю. CQRS просто означает, что вы полностью разделяете записи и чтения, занимаясь отдельными делами. У вас есть CQRS, пока вы делаете это. Ваши команды даже не должны быть асинхронными. Если у вас работают синхронные команды с отчетами об ошибках, сделайте это.
Энди
1
Команды не должны ничего возвращать в случае успеха . Идея началась с CQS , которая подразумевала, что команда все еще может выдать исключение при ошибке. То, что вы описываете, является односторонней командой. Смотрите этот ответ по отношению к этому.
Кейси

Ответы:

4

Я понимаю, что Команды не должны ничего возвращать.

Это одна точка зрения, но она не совсем в камне. Рассмотрим записи (PUT, POST, DELETE) в HTTP - все эти сообщения являются командами в том смысле, что они являются сообщениями с запросом на изменение состояния ресурса, и все же они все возвращают ответы.

Итак, как вы справляетесь с ошибкой за пределами командной шины? (Например, пользователь зарегистрировался за 1 секунду до того же имени / адреса электронной почты).

Откуда вы знаете, что команда завершилась неудачно, и как вы узнали об ошибке?

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

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

Одна идея состоит в том, чтобы подписаться на результат команды; это заимствовано из некоторых идей в паттернах корпоративной интеграции Hohpe. Основная идея заключается в том, что, поскольку клиент знаком с отправленным командным сообщением, он имеет все возможности для подписки на любые новые сообщения, опубликованные как следствие командного сообщения. Обработчик команд, после сохранения данных в книге записей, публикует события, объявляющие об успешном изменении, и клиент подписывается на эти события - распознавая правильные события, учитывая совпадение различных идентификаторов в сообщении (идентификатор причины, идентификатор корреляции и т. д.).

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

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

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

Например, пользователь зарегистрировался за 1 секунду до того же имени пользователя / адреса электронной почты

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

VoiceOfUnreason
источник
2

Например, когда вы отправляете форму регистрации, вы должны проверить в доменной модели (WriteModel в CQRS), что она находится в действительном состоянии (например, синтаксис адреса электронной почты, возраст и т. Д.)

Существует много видов проверки. Проверка при проверке синтаксиса адреса электронной почты и формата возраста - это тип проверки, который может выполнить команда. Это на самом деле не проблема домена. Может показаться, что это так, потому что некоторые эксперты в области сказали бы вам эти спецификации, но вы должны выполнить такую ​​проверку при создании команды. Фактически, общая идея состоит в том, чтобы выполнить проверку как можно скорее, потому что после того, как команда создана и отправлена ​​в BUS, становится сложнее выполнять действия.

Итак, как вы справляетесь с ошибкой за пределами командной шины? (Например, пользователь зарегистрировался за 1 секунду до того же имени / адреса электронной почты).

Этот вид валидации много обсуждается в сообществе CQRS с самого начала CQRS. Где это сделать? Это очень обсуждается. Я лично использую следующий подход: перед тем, как команда отправляется в BUS, я помечаю имя пользователя / электронную почту как принятые централизованным способом (то есть ограничение уникального индекса для имени пользователя / электронной почты). После этого я отправляю команду. Недостатком является то, что в случае сбоя команды в течение короткого периода времени это имя пользователя берется и не используется; это приемлемо для моего бизнеса, вероятно для вашего бизнеса.

Откуда вы знаете, что команда завершилась неудачно, и как вы узнали об ошибке?

Если асинхронная команда отклонена Агрегатом из-за инварианта домена, необходимо предпринять некоторые меры, выполнив некоторую компенсационную команду, которая каким-то образом уведомляет издателя команды (то есть отправляет объяснительное электронное письмо о том, что команда не выполнена).

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

Константин Гальбену
источник
1

Проверка должна быть выполнена в декораторе. Тогда любая команда, которая нуждается в проверке, может быть оформлена как таковая.

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

Другая возможность - думать о валидации как о типе «запроса», который вернул бы результат валидации. Запустите запрос проверки, а затем, если он пройден, выполните команду. Это было бы альтернативой подходу декоратора.

Джон Рейнор
источник
Мне нравится подход исключения, потому что это самый чистый способ. Но разве исключения не слишком тяжелы для этого?
EresDev
1
HI @EresDev - Да, они тяжелые. Но я не знаю, насколько «достоверны» ваши данные. Допустим, из 1000 записей 2 недействительны. Исключения могут быть жизнеспособными, поскольку они действительно исключительные условия. Теперь рассмотрим 1000 записей с 200 недействительными. В связи с этим следует избегать исключений yes, поскольку 20% ваших данных недействительны. Поэтому я оставляю на ваше усмотрение решать, выбрать этот подход или нет, исходя из вашей текущей достоверности данных. :)
Джон Рейнор
Я добавлю к этому, сказав, что если мы сохраним пример регистрационной формы, где, я думаю, 50% или даже больше пользователей сначала вставят неверные данные, исключение все равно будет в порядке. В бэкэнде веб-приложения вы в основном получаете достоверные данные, потому что веб-интерфейсы также выполняют проверку. Вы получаете, только если что-то пропустило проверку внешнего интерфейса по какому-то редкому случаю или если кто-то дурачится. Таким образом, исключения очень хорошо в этом случае.
EresDev