Вопрос в том; Как вы применяете CQS, когда вам нужен результат команды?
Ответ таков: нет. Если вы хотите выполнить команду и получить результат, вы не используете CQS.
Однако черно-белая догматическая чистота могла стать смертью вселенной. Всегда есть крайние случаи и серые области. Проблема в том, что вы начинаете создавать шаблоны, которые являются формой CQS, но больше не являются чистыми CQS.
Возможна монада. Вместо того, чтобы ваша команда возвращала пустоту, вы можете вернуть Монаду. «пустая» монада может выглядеть так:
public class Monad {
private Monad() { Success = true; }
private Monad(Exception ex) {
IsExceptionState = true;
Exception = ex;
}
public static Monad Success() => new Monad();
public static Monad Failure(Exception ex) => new Monad(ex);
public bool Success { get; private set; }
public bool IsExceptionState { get; private set; }
public Exception Exception { get; private set; }
}
Теперь вы можете использовать метод «Command», например:
public Monad CreateNewOrder(CustomerEntity buyer, ProductEntity item, Guid transactionGuid) {
if (buyer == null || string.IsNullOrWhiteSpace(buyer.FirstName))
return Monad.Failure(new ValidationException("First Name Required"));
try {
var orderWithNewID = ... Do Heavy Lifting Here ...;
_eventHandler.Raise("orderCreated", orderWithNewID, transactionGuid);
}
catch (Exception ex) {
_eventHandler.RaiseException("orderFailure", ex, transactionGuid); // <-- should never fail BTW
return Monad.Failure(ex);
}
return Monad.Success();
}
Проблема с серой областью заключается в том, что ею легко злоупотреблять. Размещение информации о возврате, такой как новый OrderID, в Monad позволит потребителям сказать: «Забудьте, ожидая Событие, у нас есть идентификатор прямо здесь !!!» Кроме того, не все команды требуют монады. Вы действительно должны проверить структуру своего приложения, чтобы убедиться, что вы действительно достигли крайнего случая.
С Monad, теперь ваше командное потребление может выглядеть так:
//some function child in the Call Stack of "CallBackendToCreateOrder"...
var order = CreateNewOrder(buyer, item, transactionGuid);
if (!order.Success || order.IsExceptionState)
... Do Something?
В кодовой базе далеко-далеко. , .
_eventHandler.on("orderCreated", transactionGuid, out order)
_storeService.PerformPurchase(order);
В графическом интерфейсе очень далеко. , .
var transactionID = Guid.NewGuid();
OnCompletedPurchase(transactionID, x => {...});
OnException(transactionID, x => {...});
CallBackendToCreateOrder(orderDetails, transactionID);
Теперь у вас есть все необходимые функциональные возможности и правильность с небольшим количеством серой области для Монады, но УБЕДИТЕСЬ, что вы случайно не выставляете плохой шаблон через Монаду, поэтому вы ограничиваете то, что можете делать с это.