Command, Command Handler and Command Executor


Command

The state of the data is altered via commands. A command handler is responsible for managing commands in CQRS. The following code snippet creates the command:

public class PlaceOrderCommand : ICommand<CommandExecutionResult<PlaceOrderResponse>>
{
    public string OrderCode { get; set; }
}

In the above code snippet, the PlaceOrderCommand implements the ICommand<T> interface, with T representing the generic type that signifies the command’s result. In the Crystal Sharp framework, the standard result type for CQRS-based commands is CommandExecutionResult<TResult>, where TResult can be of any value or reference type.

IMPORTANT

It is necessary for every CQRS-based command to implement the “ICommand interface.

CommandExecutionResult<TResult> has the following instance properties and static methods:

public bool Success { get; set; } This property will be set to true if the command executes successfully and without any issues, and to false otherwise.
public IEnumerable<Error> Errors { get; set; } Errors will be listed here if the command is not successful; otherwise, null.
public TResult Data { get; set; } This property contains the actual result, which could be a value or reference type.
public static CommandExecutionResult<TResult> WithError(IEnumerable<Error> errors) Static method, returns the CommandExecutionResult<TResult> object with errors and sets the Success property to false.

Command Handler

To handle the command, a handler is required. A command handler is where a command is actually executed. An illustration of a command handler is provided in the following code snippet:

public class PlaceOrderCommandHandler : CommandHandler<PlaceOrderCommand, PlaceOrderResponse>
{
    public override async Task<CommandExecutionResult<PlaceOrderResponse>> Handle(PlaceOrderCommand request, CancellationToken cancellationToken = default)
    {
        PlaceOrderResponse response = new() { Id = Guid.NewGuid(), OrderCode = request.OrderCode };

        return await Ok(response);
    }
}

public class PlaceOrderResponse
{
    public Guid Id { get; set; }
    public string OrderCode { get; set; }
}

The code snippet above exhibits how the PlaceOrderCommandHandler class manages the PlaceOrderCommand. It is necessary for the command handler class to inherit from the CommandHandler<TRequest, TResponse> class and override the method Handle(TRequest request, CancellationToken cancellationToken = default). The TRequest parameter in the Handle method is the command that is being handled.

The method Ok is from the base class and expects any value or reference type, which is the actual result. The Ok method returns the CommandExecutionResult<TResult> object with its Success property set to true and assigns the result to the Data property.

The CommandHandler<TRequest, TResponse> base class has a method Fail(params string[] errorMessages), which could be utilized if there is any validation error or failure.

Following code snippet calls the Fail method if the request object is null:

public class PlaceOrderCommandHandler : CommandHandler<PlaceOrderCommand, PlaceOrderResponse>
{
    public override async Task<CommandExecutionResult<PlaceOrderResponse>> Handle(PlaceOrderCommand request, CancellationToken cancellationToken = default)
    {
        if (request == null)
        {
            return await Fail("Invalid command.");
        }

        PlaceOrderResponse response = new() { Id = Guid.NewGuid(), OrderCode = request.OrderCode };

        return await Ok(response);
    }
}

public class PlaceOrderResponse
{
    public Guid Id { get; set; }
    public string OrderCode { get; set; }
}

In the above code snippet, if the request object is null, the Fail method will be invoked from the base class. This will cause the CommandExecutionResult<TResult> to be returned with its Success property set to false and the arguments of the Fail method will be assigned to the Errors property of the CommandExecutionResult<TResult> object.

Command Executor

In order to execute the command, a command executor is required, which dispatches the command to its appropriate command handler. The Crystal Sharp framework provides an interface called ICommandExecutor to execute commands. The following code snippet represents an example of a command executor:

public class OrderController : ControllerBase
{
    private readonly ICommandExecutor _commandExecutor;

    public OrderController(ICommandExecutor commandExecutor)
    {
        _commandExecutor = commandExecutor;
    }

    [HttpPost]
    public async Task<ActionResult<CommandExecutionResult<PlaceOrderResponse>>> Post([FromBody] PlaceOrderRequest request)
    {
        PlaceOrderCommand command = new() { OrderCode = request.OrderCode };

        return await _commandExecutor.Execute(command, CancellationToken.None).ConfigureAwait(false);
    }
}

The code snippet above uses constructor injection to inject the ICommandExecutor interface into OrderController. In the Post method of OrderController, a new command is created and executed by the command executor.

ICommandExecutor interface has the following method:

public Task<CommandExecutionResult<TResult>> Execute<TResult>(ICommand<CommandExecutionResult<TResult>> command, CancellationToken cancellationToken = default) Executes the command and returns the CommandExecutionResult<TResult> object. The parameter command could be any class that implements the interface ICommand<CommandExecutionResult<TResult>>. The parameter cancellationToken is optional.