Question

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?

Was it helpful?

Solution

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
Licensed under: CC-BY-SA with attribution
scroll top