问题是;当您需要命令的结果时,如何应用 CQS?
答案是:你没有。如果你想运行一个命令并返回一个结果,你没有使用 CQS。
然而,黑白教条的纯洁性可能是宇宙的死亡。总是存在边缘情况和灰色区域。问题是您开始创建作为 CQS 形式的模式,但不再是纯 CQS。
单子是一种可能性。您可以返回 Monad,而不是您的 Command 返回 void。“void” Monad 可能如下所示:
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; }
}
现在你可以有一个像这样的“命令”方法:
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 将允许消费者说,“忘记等待事件,我们在这里得到了 ID!!!” 此外,并非所有命令都需要 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);
在很远的一个GUI。. .
var transactionID = Guid.NewGuid();
OnCompletedPurchase(transactionID, x => {...});
OnException(transactionID, x => {...});
CallBackendToCreateOrder(orderDetails, transactionID);
现在你有了你想要的所有功能和适当性,只有一点点灰色区域用于 Monad,但要确保你不会意外地通过 Monad 暴露一个坏模式,所以你限制了你可以用它做的事情。