Question

What do you think about

 if(!DoSomething()) return;

In Clean Code this is viewed as violation of Command Query Separation. But how can we understand if something in command DoSomething() went wrong? What about sql command (ex: void Delete(Table))? How can we know if that table existed?

Thanks.

Was it helpful?

Solution

I agree with the comments from rObiwahn that you should check CanDoSomething before issuing a command of DoSomething. In a pure CQRS environment, DoSomething would not return anything and if anything prevented Something from happening (not due to an exception, but a race condition or something else changing between CanDoSomething and DoSomething), your domain would issue a DoSomethingWasInvalid event (or something like that) which would allow your application to eventually become consistent.

It may sound complex, but it really becomes pretty simple once you start breaking down the logic into small chunks and allow your application to embrace eventual consistency.

There are a lot of good resources on the DDD/CQRS group in google groups. A question like 'How do you tell the sender that a command failed?' is similar to your question a bit. People like Udi Dahan, Greg Young, Rinat Abdullin and others monitor that group and provide some really great answers. I'd recommend checking that out every now and then, too.

Hope this helps!

OTHER TIPS

If something went wrong, DoSomething() should probably throw an exception, if you as the caller must handle it.
E.g.:

try  
{  
  DoSomething();  
  // .. do more after success  
}  
catch(SomeException ex) // maybe focus on a special error
{
  // maybe do something special or just clean up!  
}  

I will write this example in Eiffel to make it simple to understand.

my_code
      -- Calls the `do_something' routine
   do
      set_table ("my_table")
      do_something
   end

do_something
      -- Something to do
   require
      valid_table: is_valid_table (table_name)
   do
      sql_list := execute_sql_on_table (table_name)
   ensure
      has_result: sql_list.count > 0
   end

sql_list: ARRAYED_LIST [STUFF]

table_name: STRING

set_table (a_name: STRING)
       -- Set `table_name' to `a_name'
   require
      has_name: not a_name.is_empty
      valid_table: is_valid_table (a_name)
   do
      table_name := a_name
   ensure
      table_name_set: table_name.same_string (a_name)
   end

delete_table (a_name: STRING)
      -- Delete `a_name' from the database.
   require
       valid_table: is_valid_table (a_name)
   do
      execute_sql ("DROP TABLE " + a_name)
   ensure
       table_gone: not is_valid_table (a_name)
   end
  1. The feature `do_something' is a command, where the array sql_list is to be loaded with STUFF from the table "my_table".
  2. The precondition contract on do_something' makes it the responsibility of the clientmy_code' to provide table_name' before making the call todo_something'.
  3. In return, the post-condition ensure contract makes it the responsibility of the supplier do_something' fill the arraysql_list' with instances of STUFF.
  4. The feature `sql_list' is a query, returning a reference pointer to an array of STUFF.
  5. Similarly, the feature table_name' is a query returning a reference pointer to a STRING, which is set with a "setter" command calledset_table'.

In this case, the "contracts" of Design-by-Contract take care of ensuring appropriate separation of concerns and who is responsible for what in this small bit of code above. Notice the distinct lack of TRY-CATCH constructs in the code. In this case, the data source is expected to have "my_table". The presence of the contracts means the software will raise an exception when the contract fails. A require failure says the caller is broken, while a failure in an ensure post-condition points at the supplier feature.

Finally, this code exhibits clear Command-Query-separation and the application of quality assurance gained from Design by Contract. Thus, the original question can be answered:

"But how can we understand if something in command DoSomething() went wrong? What about sql command (ex: void Delete(Table))? How can we know if that table existed?"

While it might be true that a call to delete_table ("my_table") might be injected into some ancestor or might happen on another thread, this is what the contracts are for in do_something'. As long as those contracts stand guard over calls todo_something', the process will be appropriately handled. An injected call to `delete_table' will simply cause the contract to fail.

All of this is presuming it is NOT OK to DROP TABLE on "my_table" and that doing so is tragically accidental. However, if it becomes OK to DROP TABLE on "my_table" then a retry mechanism or other "handler" is needed to manage this use case and the code above will not work.

i really had this thing came to my mind now, [why we don't think of "concerns of separation" rather than "separation of concerns"!] i know its off topic but too much push for standards will lead to NO WHERE. from history of mankind it easy to see how much of practices/standards proven wrong with time! it's all about your perspective of excellence.

so in such case i always try to think of the opposite to stay REAL. i want to say more but i think that's enough from my perspective of answering ;)

good luck!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top