This post looks at how we can make it absolutley clear that exceptions may occur when executing commands. Also, we wish to force the developer to a least think of what to do.
If we look at the previous posts on this blog by Yngve on the topic of commands and domain events, the commands executor will return a CommandResult with an enum indicating success or failure when executing the command. Thus swallowing the exception, passing only the message onwards.
public CommandResult Execute(Command command)
{
dynamic handler = FindHandlerForCommand(command);
try
{
handler.Handle(command);
return CommandResult.Executed();
}
catch(Exception exception)
{
return CommandResult.Failed(exception.Message);
}
}
[HttpPost]
public ActionResult Register(RegisterANewUserViewModel viewModel)
{
// perform viewmodel validation first!
var result = _commandExecutor.Execute(cmd =>
{
cmd.Username = viewModel.Username;
cmd.Password = viewModel.Password;
cmd.Email = viewModel.Email;
});
//at this point our developer forgets to check result and just continues flow
}
This may be perfectly fine if all developers using the system are aware of this and take appropriate action. However, if we know that our system may be used by other developers not aware of the pattern, or we have a bad day and don't check the command result ourself, there's no guarantee a failure will be dealt with.
This leads to our little exercise, how can we make it abundantly clear that commands may fail? And also force the developer to handle it, or at least think of handling it. Enter Action <Exception> delegate.
public void Execute(Command command, Action error)
{
dynamic handler = FindHandlerForCommand(command);
try
{
handler.Handle(command);
}
catch(Exception exception)
{
error(exception);
}
}
[HttpPost]
public ActionResult Register(RegisterANewUserViewModel viewModel)
{
// perform viewmodel validation first!
var result = _commandExecutor.Execute(cmd =>
{
cmd.Username = viewModel.Username;
cmd.Password = viewModel.Password;
cmd.Email = viewModel.Email;
}, error =>
{
/* Deal with error */
});
// continue flow
}
Notice the signature of the Execute method. By this it is clear for developers using the command executor they must deal with the exceptions as the execute method takes a delegate parameter for error handling.