Проверка команды в DDD с CQRS
Я изучаю DDD и использую шаблон CQRS. Я не понимаю, как проверять бизнес-правила в обработчике команд без чтения из хранилища данных.
Например, Крис хочет дать Эшли подарок.
Командой может быть команда GiveGiftCommand.
В какой момент я бы подтвердил, что Крису действительно принадлежит подарок, который он хочет дать? И как бы я это сделал, не читая из базы данных?
Ответы
Ответ 1
Существуют разные мнения и мнения относительно проверки в обработчиках команд.
Команда может быть отклонена, мы можем сказать " Нет" команде, если она недействительна.
Обычно у вас есть проверка, которая возникает в пользовательском интерфейсе, и которая может быть дублирована внутри обработчика команд (некоторые люди тоже склонны помещать их в домен). Затем обработчик команд может запускать простую проверку, которая может возникать вне объекта, например, данные в правильном формате, есть ожидаемые значения и т.д.
С другой стороны, бизнес-логика не должна быть в обработчике команд. Он должен быть в вашем домене.
Поэтому я считаю, что основной вопрос...
Должен ли я запрашивать чтение из командных обработчиков?
Я бы сказал, нет. Не используйте модель чтения в обработчиках команд или логике домена. Но вы всегда можете запросить свою модель чтения у клиента, чтобы получить данные, необходимые для вашей команды, и проверить эту команду. Вы бы запросили читающую сторону на клиенте, чтобы проверить, будет ли Крис действительно подарок, который он хочет дать. Конечно, валидация с использованием модели чтения, вероятно, будет в конечном итоге последовательной, что, конечно же, является еще одной причиной отказа команды от агрегата внутри обработчика команд.
Некоторые люди не согласны с тем, что если вам потребуются ваши команды для хранения данных, которые необходимо обработать обработчику, то вы не сможете изменить логику проверки в своем обработчике/домене, не затрагивая клиента. Это предоставляет слишком много знаний о домене клиенту и противоречит тому, что клиент хочет выразить намерение. Таким образом, они будут стремиться предоставить интерфейс GiftService
(который является частью вездесущего языка) вашему обработчику команд, а затем реализовать интерфейс по мере необходимости, что может включать в себя запрос на чтение.
Я думаю, что клиент должен всегда предполагать, что команды, которые он выдает, будут успешными. Вызов стороны чтения для проверки команды не требуется. Получение двух противоречивых команд очень маловероятно (пользователи создают учетные записи с тем же адресом электронной почты). Тогда вы должны иметь возможность выпустить корректирующие действия, например, Saga/Process Manager. Поэтому вместо принятия корректирующих действий было бы менее проблематично, если бы команда могла быть проверена и не отправлена в первую очередь.
Ответ 2
Это зависит от того, является ли операция асинхронной или нет, то есть пользователь ожидает немедленный ответ. Владение подарками - это в основном функция безопасности, и это может быть сделано как операция "prep" перед вызовом фактического сервиса или отправкой GiveGiftCommand.
Единственная проверка команды, которую вы можете сделать, состоит в том, чтобы убедиться, что она содержит данные в требуемом формате (проверка UI) и что у пользователя есть разрешения на выполнение этого действия. Но после того, как команда отправлена в домен, чтобы решить, соблюдаются ли другие бизнес-ограничения.
Если пользователь ожидает немедленную обратную связь, вы можете "подождать" до тех пор, пока команда не будет завершена, и для этого вы можете использовать подход, в котором обработчик команды может предоставить результат отправителю с помощью посредника. Но это означает, что по крайней мере некоторые команды выполняются незамедлительно, и это может быть не так в вашем приложении. Однако это самый простой подход, если вы хотите просто вернуть ошибку сообщения, а не выполнять компенсацию и другие вещи. Некоторые примеры использования просты.
О обработчиках команд и бизнес-логике я не согласен с Томашем Яскуа. Обработчик команд - это функция, техническая деталь. Вы можете поместить бизнес-логику в обработчик команд или статическую функцию, это не имеет значения. Сообщения и их обработчики являются инфраструктурными компонентами, которые могут использоваться для реализации функциональных возможностей. Например, в приложении вы можете иметь события домена, события приложений и т.д. Это все события, т.е. Уведомление о том, что что-то изменилось, и вы можете иметь обработчики событий, которые находятся в домене или в других местах.
Там нет правила, запрещающего вам "читать" из db, но, по крайней мере, теоретически модель чтения устарела. Однако это может не быть такой проблемой в 99% случаев. Для остальных 1% вам нужны очень конкретные решения.
Ответ 3
Я просто задал один и тот же вопрос у знакомого моего знакомого, и его ответ состоял в том, что это нормально, чтобы выполнить эту проверку внутри обработчика команд (в моем случае внутри постоянного актера akka, который интерпретирует команды и записывает события в журнал).
Однако, если это невозможно по соображениям производительности (поскольку проверка обработки внутри постоянного участника блокирует актера, и это будет узким местом масштабируемости для всего приложения), тогда можно использовать оптимистичную блокировку (OCC).
Другими словами, проверка может выполняться в другом акторе (позволяет называть его актором валидатора, который не находится в постоянном акторе), это не будет блокировать постоянный актер, но может случиться так, что данные, которые используются для проверки, изменен в постоянном акторе, в то время как проверка выполнялась в актере валидатора).
Если актор валидатора возвращается с ОК, и все данные, которые были использованы для проверки, все равно одинаковы для постоянного участника (имеет ту же версию - версию, что и в OCC), как это было в момент, когда команда прибыла в обработчик команд (постоянный актер), то команда принимается постоянным игроком, в противном случае валидация должна быть повторно отправлена для переоценки актеру-валидатору.