The Simple Approach
Commands
A Command is a class that contains the data required to perform the business process. For example you might have an EditCustomerName Command that contains the ID of the Customer to be edited and their new name.
public class EditCustomerName : ICommand { public int CustomerId { get; set; } public string NewName { get; set; } }
Handlers
Each Command is handled by a (you guessed it) Command Handler. To handle the above Command we might have something like this:
public class EditCustomerNameHandler : ICommandHandler<EditCustomerName> { public async Task HandleAsync(EditCustomerName command) { // Do stuff } }This leads to much more focused code as each handler is responsible for just one business process. So if there are seven different operations that can be performed on a Customer, there will be seven different Handlers, each responsible for its own Command. In the service approach, the CustomerService would have had seven (unrelated) methods, which leads to hard to understand (and therefore hard to maintain) code.
Results
Strict interpretations of the Command Pattern often say that you shouldn't return anything from the processing of a Command. I find this to be unrealistic as it's often necessary to communicate (at the very least) the success or failure of the operation. So for the simple approach the return object (including convenience factory methods) I used was this:
public class CommandResult { public bool IsSuccess { get; } public IEnumerable<string> Errors { get; } public CommandResult(bool isSuccess, params string[] errors) { IsSuccess = isSuccess; Errors = errors; } public static CommandResult Success() { return new CommandResult(true); } public static CommandResult Error(params string[] errors) { return new CommandResult(false, errors); } }
Summing Up
Pulling together the ideas above we can define a Command Handler interface as follows:
public interface ICommandHandler<T> where T : ICommand { Task<CommandResult> HandleAsync(EditCustomerName command); }This simple approach allows each business process to be handled by its own class, and for any errors to be communicated back to the client.
There are however a number of limitations, such as difficulty composing different Command Handlers together and no way to communicate data back to client. I'll look to address these limitations in the next few posts.
No comments:
Post a Comment