0

In Domain-Driven Design (Eric Evans) the discussion on side-effect-free functions talks about separating commands which "result in modifications to observable state" from queries which have no side-effects.

Then there's this part (emphasis mine):

Strictly segregate commands into very simple operations that do not return domain information.

What is the importance of not returning domain information in this context?

  • 3
    Returning domain information would be a query. – Robert Harvey Jun 12 at 16:25
  • 1
    Another way to think about commands is to compare them to Updates, Deletes and Inserts when applied to a database. Those usually don't return you anything about the tables they changed except by "Changed X records" or something of the sort. Unless you do something very... exotic, it isn't common to do ask the database to a insert a new Employee record and be slapped back in the face with the last 25 employees named "John". – T. Sar Jun 12 at 16:58
2

What is the importance of not returning domain information in this context?

It's a restatement of Command Query Separation, proposed by Bertrand Meyer.

Asking a question should not change the answer.

In particular, you shouldn't have to change the state of the program to ask a question about it.

You’re not really answering why it is important not to return information from a command. If you think about it, it’s fairly obvious not to change state when doing a query, but why is it a bad practice for a command to return (new) state?

I think it's a classification issue: ie, by definition a method that returns a value is a query. In other words, mechanics trumping semantics.

But perhaps this historical perspective will help.

In the late 90s, C++ was a popular language, and exception safety was an important design constraint -- how do you ensure recovery when transactions can fail? When exceptions could not be eliminated from the design, the goal became strong exception safety: in the face of an exception, the program should rollback to a known good state.

One case where an exception could occur is during the copy of an object; we have some object that needs a lot of heap, try to copy that object, but there's not enough heap left to support a second instance. BOOM.

Which meant that, in general, some operations could throw when returning the result.

Riddle: how to create an implementation of T Stack<T>::pop() that was strongly exception safe?

And the answer is... it cannot be done. You modified the internal data structure of the stack to remove the desired element, the attempt to return the value throws, and you've got no way to put the stack back into its original state.

Instead, you had to change the design of the stack api, so that you have better control of where the modifications of the stack actually happen relative to the dangerous copy. For example

T Stack<T>::top() // Does not change the stack!
void Stack<T>::pop() // Does not perform a dangerous copy
  • You’re not really answering why it is important not to return information from a command. If you think about it, it’s fairly obvious not to change state when doing a query, but why is it a bad practice for a command to return (new) state? – Rik D Jun 14 at 7:19
  • @RikD Can you explain the difference between a "command" that returns state and a "query" that changes state? – king-side-slide Jun 14 at 15:51
  • The name of the method. You don’t expect a method called SearchUser to change state. A method called CreateUser, that returns the Id for the newly created user feels more reasonable. – Rik D Jun 14 at 19:08
  • @RikD The OP is asking about behavior, not how to name things. And while naming is important, it is the behavior of a method that we use as a means for classification. For example, if I saw a signature like public void SearchUser(...) I would assume state change (the assumption being the domain is concerned with storing search information). Similarly, given a signature like public User CreateUser(...) I would be more inclined to understand the method as a factory than a command. – king-side-slide 2 days ago
  • Good explanation @king-side-slide. How do you handle commands that can fail? Throw an exception? I often use a Result for a command that can fail, because I’d like to reserve exceptions for truly exceptional situations. Do you think returning a Result for a command and a Result<T> for a query violates CQS? – Rik D yesterday

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.