command

What Are Command Functions?

command

Photo by insung yoon on Unsplash

 

Command and Queries Functions comprise the two complementary behaviours of Command Query Responsibility Separation or CQRS. While Query functions measure and report on the state of a system, Command functions, or Commands for short, change a system’s state. 

Command Functions

How do Command Functions alter the state of a system? Simple, they receive a Command Message containing information that will update the system state. 

In a broader sense, Command Messages could be an HTTP POST payload, an event in an event store, or one or more parameters in a function call.

Command functions intend to change the state of the system. They create a Side Effect. Unlike queries, commands do not enquire as to the state of the system, and therefore no return value should be required—Commands return void. Well, usually—we’ll come to that. 

 

Example Commands:

  bool IsActive { set; }     // C# property setter called via assignment. Eg. IsActive = true;
  void Update(CustomerRevision revision);
  void SetOrderStatus(OrderStatus newStatus)

In a minority of cases, it does make sense for Commands to return a value:

  Customer  Register(CustomerRegistation registration);

In this example, if the call to register a new customer succeeds, it will presumably have saved a new customer record, including an identifier to a persistent data store for later retrieval. It is only appropriate that this command function informs the caller of the newly created customer record, including the internally generated customer identifier, so we can reference this customer in other operations—for example, when the customer makes a purchase. 

Errors

However, we do not require a return value from a Command Function to communicate a problem or error in program operation. 

To illustrate, let’s consider what a high-level method to register a new customer might look like using a hybrid return structure which combines the customer, in case of successful registration, and an error message describing a failure:

  public class RegisterCustomerResult
  {
     public bool IsSuccess { get; set; }
     public string ErrorMessage { get; set; }
     public Customer Customer { get; set; }
  }

And

  public RegisterCustomerResult RegisterCustomer(CustomerRegistration registration)
  {
     var validationResult = Validate(registration);
     if (validationResult != ValidationStatus.OK)
        return new RegisterCustomerResult { IsSuccess = false, ErrorMessage = validationResult.Error };
     var customer = registration.ToCustomer();
     var dbResult = Repository.Save(customer);
     if (dbResult != DbReturnCode.SuccessOK)
        return new RegisterCustomerResult { IsSuccess = false, ErrorMessage = dbResult.ErrorMessage };
     return new RegisterCustomerResult { IsSuccess = true, Customer = customer };
  }

The RegisterCustomer() command method is noticeably busy, and it isn’t easy to understand what’s happening. The result code checks are to blame. 

Error return codes are out. I recommend you avoid them.

Maybe we can do better.

How should a Command function communicate errors back to its caller?

The answer is with Exceptions.

Exceptions

Commands throw exceptions to signal errors to the calling function. This approach simplifies our code by removing the need for result code checks.

Here is an alternative version of the RegisterCustomer() method, but this time Validate() and Repository.Save() communicating errors via exceptions:

public Customer RegisterCustomer(CustomerRegistration registration)
{
    Validate(registration);
    var customer = registration.ToCustomer();
    Repository.Save(customer);
    return customer;
}

How much simpler is that? A lot, I think. The return code checking in the previous example masked the real action that is so clear in the second: Validation, Conversion, Persistence, Return Customer, Done!

Command Functions should communicate errors via Exceptions

 

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply