In wikipedia\'s definition of command query separation, it is stated that
More formally, methods should return a value only if they are referentially t
This question is old but has not received a satisfying answer yet, so I'll elaborate a bit on my comment from almost a year ago.
Using an event driven architecture makes a lot of sense, not only for achieving clear command/query separation, but also because it opens new architectural choices and usually fits with an asynchronous programming model (useful if you need to scale your architecture). More often than not, you will find the solution may lie in modelling your domain differently.
So let's take your purchase example. StoreService.ProcessPurchase
would be a suitable command for processing a purchase. This would generate a PurchaseReceipt
. This is a better way instead of returning the receipt in Order.Result
. To keep things very simple, you can return the receipt from the command and violate CQRS here. If you want a cleaner separation, the command would raise a ReceiptGenerated
event that you can subscribe to.
If you think about your domain, this may actually be a better model. When you're checking out at a cashier, you follow this process. Before your receipt is generated, a credit card check might be due. This is likely to take longer. In a synchronous scenario, you would wait at the cashier, unable to do anything else.
I see a lot of confusion above between CQS & CQRS (as Mark Rogers noticed at one answer as well).
CQRS is an architectural approach in DDD where, in case of a query, you do not build up full blown object graphs from aggregate roots with all their entities and value types, but just lightweight view objects to show in a list.
CQS is a good programming principle on code level in any part of your application. Not just the domain area. The principle exists way longer than DDD (and CQRS). It says not to mess up commands that change any state of the application with queries that just return data and can be invoked any time without changing any state. In my old days with Delphi, the lanquage showed a difference between functions and procedures. It was considered a bad practice to code 'functionprocedures' as we called them back than as well.
To answer the question asked: One could think of a way to work around executing a command and getting back a result. For instance by providing a command object (command pattern) which has a void execute method and a readonly command result property.
But what is the main reason to adhere to CQS? Keep code readable and reusable without the need to look at implementation details. Your code should be trustworthy not to cause unexpected side effects. So if the command wants to return a result, and the function name or return object clearly indicates that it is a command with a command result, I'll accept the exception to the CQS rule. No need to make things more complex. I agree with Martin Fowler (mentioned above) here.
By the way: wouldn't strictly following this rule break the whole fluent api principle?
The question being; How do you apply CQS when you need the result of a command?
The answer is: You don't. If you want to run a command and get back a result, you aren't using CQS.
However, black and white dogmatic purity could be the death of the universe. There are always edge cases and grey areas. The problem is that you begin to create patterns that are a form of CQS, but no longer pure CQS.
A Monad is a possibility. Instead of your Command returning void, you could return Monad. a "void" Monad might look like this:
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; }
}
Now you can have a "Command" method like so:
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();
}
The problem with grey area is that it is easily abused. Putting return information such as the new OrderID in the Monad would allow consumers to say, "Forget waiting for the Event, we've got the ID right here!!!" Also, not all Commands would require a Monad. You really should check the structure of your application to ensure you have truly reached an edge case.
With a Monad, now your command consumption might look like this:
//some function child in the Call Stack of "CallBackendToCreateOrder"...
var order = CreateNewOrder(buyer, item, transactionGuid);
if (!order.Success || order.IsExceptionState)
... Do Something?
In a codebase far far away . . .
_eventHandler.on("orderCreated", transactionGuid, out order)
_storeService.PerformPurchase(order);
In a GUI far far away . . .
var transactionID = Guid.NewGuid();
OnCompletedPurchase(transactionID, x => {...});
OnException(transactionID, x => {...});
CallBackendToCreateOrder(orderDetails, transactionID);
Now you have all of the functionality and properness you want with just a bit of grey area for the Monad, but BEING SURE that you aren't accidentally exposing a bad pattern through the Monad, so you limit what you can do with it.
Oh that's interesting. Probably I have something to say, too.
During recent time I've been using non-orthodox CQS (maybe not CQS at all for somebody, but I don't really care) approach which helps to avoid messy Repository (because who uses the specification pattern, huh?) implementations and Service layer classes which grow up absolutely enormously over time, especially in huge projects. The problem is it happens even if everything else is fine and developers are pretty skilled, because (surprise) if you have a big class it doesn't always mean it violates SRP in the first place. And the common approach I see in such projects very often is "Oh, we've got huge classes, let's divide them", and that division is mostly synthetic rather than evolving naturally. So, what do people do to cope with this? They make several classes out of one. But what happens with DI in a huge project when you suddenly have several times more classes than before? Not really nice picture since DI is probably already pretty loaded with injections. So there come workarounds as facade pattern etc. (when applicable), and the implications are that we: don't prevent the problem; deal with consequences only and spend much time for it; often use "synthetic" approach to refactoring; get less evil instead of more evil, but still that's evil.
What do we do instead? We apply KISS and YAGNI to CQS as a first step.
What problems are solved with this approach?
So what would I suggest you to do?