Здається, існує нескінченна плутанина щодо того, чи повинні команди мати чи не мати повернені значення. Я хотів би знати, чи плутанина просто тому, що учасники не вказали свого контексту чи обставин.
Плутанина
Ось приклади плутанини ...
Уді Дахан каже, що команди "не повертають помилки клієнту", але в тій же статті він показує схему, де команди дійсно повертають помилки клієнту.
Стаття Microsoft Press Store говорить "команда ... не повертає відповідь", але потім надає неоднозначне застереження:
У міру зростання досвіду бойових дій навколо CQRS, деякі практики консолідуються і, як правило, стають найкращими практиками. Частково всупереч тому, що ми щойно заявили ... сьогодні поширеною є думка, що і обробник команд, і програма повинні знати, як проходила транзакційна операція. Результати повинні бути відомі ...
- Джиммі Богард каже, що " команди завжди мають результат ", але потім докладає додаткових зусиль, щоб показати, як команди повертаються недійсними.
Ну, обробники команд повертають значення чи ні?
Відповідь?
Беручи підказку з " Міфів про CQRS " Джиммі Богарда , я думаю, відповідь (и) на це питання залежить від того, про який програмний / контекстний "квадрант" ви говорите:
+-------------+-------------------------+-----------------+
| | 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 - це, зрештою, інструмент пам'яті. Враховуючи все це, я думаю, що причина, за допомогою якої Джиммі обережно витратив час на повернення порожнього результату від команди, полягала не в тому, що "обробники команд не повинні мати повернених значень", а тому, що він просто хотів, щоб його клас Посередник мав послідовний інтерфейс:
public interface IMediator
{
TResponse Request<TResponse>(IQuery<TResponse> query);
TResult Send<TResult>(ICommand<TResult> query); //This is the signature in question.
}
... навіть незважаючи на те, що не всі команди мають значуще значення для повернення.
Повторіть і заверніть
Чи правильно я фіксую, чому в цій темі плутанина? Щось мені не вистачає?
Оновлення (6/2020)
За допомогою поданих відповідей, я думаю, я розплутав плутанину. Простіше кажучи, якщо команда CQRS здатна повернути успіх / помилку, що вказує на стан завершення , тоді значення повернення має сенс. Це включає повернення нової ідентифікації рядка БД або будь-який результат, який не читає та не повертає вміст моделі домену (бізнесу).
Я думаю, що там, де виникає плутанина "команди CQRS", йдеться про визначення та роль "асинхронності". Існує велика різниця між асинхронним введенням-виводом "на основі завдань" та асинхронною архітектурою (наприклад, проміжне обладнання на основі черги). У першому "завдання" асинхронізації може і надасть результат завершення команди async. Однак команда, надіслана RabbitMQ, не отримає аналогічним чином повідомлення про завершення запиту / відповіді. Саме цей останній контекст асинхронної архітектури змушує деяких говорити "не існує такого поняття, як асинхронна команда" або "команди не повертають значень".