Кажется, существует бесконечная путаница в отношении того, должны ли команды иметь или не иметь возвращаемых значений. Я хотел бы знать, является ли путаница просто потому, что участники не указали свой контекст или обстоятельства.
Путаница
Вот примеры путаницы...
-
Udi Dahan говорит, что команды "не возвращают ошибки клиенту", но в той же статье он показывает диаграмму, где команды действительно возвращают ошибки клиенту.
-
В статье Microsoft Press Store говорится, что "команда... не возвращает ответ", но затем продолжает двусмысленно:
По мере того как опыт битвы растет вокруг CQRS, некоторые практики консолидируются и, как правило, становятся лучшими практиками. Отчасти противоречит тому, что мы только что заявили... Сегодня принято считать, что и обработчик команд, и приложение должны знать, как прошла транзакционная операция. Результаты должны быть известны...
- Джимми Богард говорит: " команды всегда имеют результат", но затем прилагает дополнительные усилия, чтобы показать, как команды возвращают void.
Ну, обработчики команд возвращают значения или нет?
Ответ?
Взятие реплики от Джимми Богарда Мифы о 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.
}
... хотя не все команды имеют значимое значение для возврата.
Повторить и обернуть
Я правильно понимаю, почему существует путаница в этой теме? Что-то мне не хватает?