Подтвердить что ты не робот

CQRS: возвращаемые значения команд

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

Путаница

Вот примеры путаницы...

  • Udi Dahan говорит, что команды "не возвращают ошибки клиенту", но в той же статье он показывает диаграмму, где команды действительно возвращают ошибки клиенту.

  • В статье Microsoft Press Store говорится, что "команда... не возвращает ответ", но затем продолжает двусмысленно:

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

Ну, обработчики команд возвращают значения или нет?

Ответ?

Взятие реплики от Джимми Богарда Мифы о QQRS: "Я думаю, что ответ на этот вопрос зависит от того, какой программный/контекстуальный" квадрант ", о котором вы говорите:

+-------------+-------------------------+-----------------+
|             | Real-time, Synchronous  |  Queued, Async  |
+-------------+-------------------------+-----------------+
| Acceptance  | Exception/return-value* | <see below>     |
| Fulfillment | return-value            | n/a             |
+-------------+-------------------------+-----------------+

Принятие (например, проверка)

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

Однако, похоже, многие практикующие не инициируют проверку изнутри обработчика команд. Из того, что я видел, это либо потому, что (1) они уже нашли фантастический способ обработать проверку на уровне приложения (т.е. Контроллер ASP.NET MVC, проверяющий правильное состояние посредством аннотаций данных) или (2) архитектуру, который предполагает, что команды отправляются на (вне процесса) шину или очередь. Эти последние формы асинхронности обычно не предлагают синхронной семантики проверки или интерфейсов.

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

Выполнение

Что касается "выполнения" команды, клиенту, который выдал эту команду, возможно, потребуется знать идентификатор области видимости для вновь созданной записи или, возможно, информацию об отказе, такую ​​как "сбой учетной записи".

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

Здесь можно свести всю путаницу:

Многие (большинство?) практикующих CQRS предполагают, что они теперь или в будущем будут включать асинхронную инфраструктуру или платформу (шину или очередь) и, таким образом, объявить, что обработчики команд не имеют возвращаемых значений. Тем не менее, некоторые практикующие не намерены использовать такие конструкторы, управляемые событиями, и поэтому они будут одобрять обработчики команд, которые (синхронно) возвращают значения.

Так, например, я считаю, что синхронный (запрос-ответ) контекст принимался, когда Джимми Богард предоставил этот примерный командный интерфейс:

public interface ICommand<out TResult> { }

public interface ICommandHandler<in TCommand, out TResult>
    where TCommand : ICommand<TResult>
{
    TResult Handle(TCommand command);
}

Его продукт Mediatr - это, в конце концов, инструмент с памятью. Учитывая все это, я думаю, что причина, по которой Jimmy тщательно потратила время на получение возврата void из команды, была не потому, что "обработчики команд не должны были возвращаться значения", но вместо этого он просто хотел, чтобы его класс Mediator имел согласованный интерфейс:

public interface IMediator
{
    TResponse Request<TResponse>(IQuery<TResponse> query);
    TResult Send<TResult>(ICommand<TResult> query);  //This is the signature in question.
}

... хотя не все команды имеют значимое значение для возврата.

Повторить и обернуть

Я правильно понимаю, почему существует путаница в этой теме? Что-то мне не хватает?

4b9b3361

Ответ 1

Следуя советам "Сложность в CQRS" Владика Хононова предполагает, что обработка команд может возвращать информацию, относящуюся к ее результату.

Без нарушения каких-либо принципов [CQRS] команда может безопасно вернуть следующие данные:

  • Результат выполнения: успех или неудача;
  • Сообщения об ошибках или ошибки проверки в случае сбоя;
  • Агрегат нового номера версии, в случае успеха;

Эта информация значительно улучшит работу вашей системы, потому что:

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

Даниэль Уиттакер выступает за возвращение объекта общий результат" из обработчика команд, содержащего эту информацию.

Ответ 2

Ну, обработчики команд возвращают значения или нет?

Они не должны. CQRS CQS выведен на более высокий уровень. Даже если вы нарушите правила пуриста и вернете что-нибудь, что бы вы вернули? В CQRS обработчик команд - это метод application service, который загружает aggregate, затем вызывает метод на aggregate, после чего он сохраняет aggregate. Целью обработчика команды является изменение aggregate. Вы не знали бы, что вернуть, что будет независимо от вызывающего. Каждый обработчик/клиент обработчика команд хотел бы узнать что-то еще о новом состоянии.

Если выполнение команды блокируется (aka synchronous), тогда вам нужно будет узнать, успешно ли выполнена команда или нет. Затем на более высоком уровне вы будете запрашивать то, что вам нужно знать о состоянии нового приложения, используя модель запроса, которая наилучшим образом соответствует вашим потребностям.

Подумайте иначе, если вы вернете что-то из обработчика команд, вы даете ему две обязанности: 1. измените состояние агрегата и 2. запросите некоторую модель чтения.

Что касается проверки команды, существует как минимум два типа проверки команды:

  • проверка работоспособности команды, которая проверяет правильность данных команды (т.е. адрес электронной почты); это выполняется до того, как команда достигнет совокупности, в обработчике команд (службе приложения) или в конструкторе команд;
  • проверка инвариантов домена, которая выполняется внутри агрегата, после того, как команда достигает агрегата (после вызова метода в агрегате), и он проверяет, может ли агрегат переходить в новое состояние.

Однако, если мы продвинемся на уровень выше, в Presentation layer (т.е. конечная точка REST), клиент Application layer, мы можем вернуть что угодно, и мы не будем нарушать правила, потому что конечные точки создаются после использования, вы точно знаете, что хотите вернуть после выполнения команды, в каждом случае.