Command Pattern

The command pattern is also one of the design patterns from the Gang Of Four design patterns. The command pattern is a behavioral design pattern.

And the primary intent of this pattern is to encapsulate a request as an object, thereby letting us parameterize clients with different requests queue or log requests and support undoable operations.

From the above definition, it is not very clear what the purpose of the command pattern is. In very simple language, this pattern mainly helps decouple the invoker of a request from the receiver of the request.

The use case for Command Pattern

Let us understand this pattern through an example. In this example, we will have a distributed system. And in this distributed system, we will have two main services.

  • The first one is order service
  • And the second one is inventory service.

Proxy Classes

Firstly, I will create proxy classes, which will be the receiver of a request in the pattern.

And we will have a couple of proxy classes for the order service in the inventory service, and they are InventoryProxy and OrderProxy.

Both the InventoryProxy class and OrderProxy class will implement the IProxy interface.

The code

namespace CommandPattern.Demo;
public interface IProxy<T>
{
    bool Create(T item);
    bool Delete(T item);
}

public class InventoryProxy : IProxy<Inventory>
{
    public bool Create(Inventory inventory)
    {
        Console.WriteLine($"Created inventory for product: {inventory.ProductName}");
        return true;
    }

    public bool Delete(Inventory inventory)
    {
        Console.WriteLine($"Deleted inventory for product: {inventory.ProductName}");
        return true;
    }
}

public record Inventory(string ProductName, int Quentity);

public class OrderProxy : IProxy<Order>
{
    public bool Create(Order order)
    {
        Console.WriteLine($"Created order for product: {order.ProductName}");
        return true;
    }

    public bool Delete(Order order)
    {
        Console.WriteLine($"Deleted order for product: {order.ProductName}");
        return true;
    }
}

public record Order(string ProductName, int Quantity);

Explanation of proxy classes

The IProxy interface has two methods, Create and Delete. And for both methods, the parameter is a generic type. For the InventoryProxy class, this parameter will be of record type Inventory. And for the OrderProxy class, this parameter will be of record type Order.

For the simplicity of the example, in these methods, I am just doing Console.WriteLine. But as you can understand in a real-life scenario these methods will be calling out to another couple of HTTP services to execute some functionality.

The Orchestrator

Now, we want to make an orchestrator, which will be responsible for creating an order and then creating an inventory. But in case of any error with creating inventory, it is going to roll back the order.

One way to do this is for the orchestrator to directly call inventory and order proxy classes. Or the better approach will be to decouple the orchestrator from the actual implementation. And this is where we can implement the command pattern.

For the command design pattern, there are four main components.

  • The first component is a command which decouples the receiver from the invoker
  • And then the second is the invoker which will be invoking the command
  • Thirdly, the receiver is the one who will be finally executing the command
  • Fourthly, there is a concept of a client who will create the command

And in this example, the OrderProxy and the InventoryProxy are nothing but the receivers.

The command classes

namespace CommandPattern.Demo;

public interface IMessage { }

public interface ICommand
{
    bool Execute(IMessage message);
    bool Rollback(IMessage message);
}

The interface ICommand has two methods Execute and Rollback. And both of these methods will return a boolean and has a single parameter of type IMessage. From an orchestrator’s point of view, it is just going to either execute or roll back if the execution is unsuccessful.

And IMessage is just an empty type because what we are going to do is in the proxy classes the records Inventory and Order are going to implement the IMessage interface.

public record Inventory(string ProductName, int Quentity) : IMessage;
public record Order(string ProductName, int Quantity) : IMessage;

Next, I am going to create concrete command classes for inventory and order. For the command pattern, the command class is the one that decouples the invoker from the receiver. And in this case, the receiver is nothing but the proxy classes.

OrderCommand class

public class OrderCommand : ICommand
{
    private readonly IProxy<Order> orderProxy;

    public OrderCommand(IProxy<Order> orderProxy)
    {
        this.orderProxy = orderProxy;
    }

    public bool Execute(IMessage message)
    {
        return orderProxy.Create((Order)message);
    }

    public bool Rollback(IMessage message)
    {
        return orderProxy.Delete((Order)message);
    }
}

The constructor of the OrderCommand class will take IProxy<Order> as an input parameter. Next, inside the Execute method, we will call the Create method of the OrderProxy. And inside the Rollback method, we will call the Delete method of the OrderProxy. In both cases, we will typecast the IMessage to the record type Order.

During Rollback we call Delete because it’s a compensating transaction. And that’s what happens when you are dealing with a distributed transaction, we don’t roll back, we just perform a compensating transaction.

InventoryCommand class

We are going to have a similar class for the inventory command.

public class InventoryCommand : ICommand
{
    private readonly IProxy<Inventory> inventoryProxy;

    public InventoryCommand(IProxy<Inventory> inventoryProxy)
    {
        this.inventoryProxy = inventoryProxy;
    }

    public bool Execute(IMessage message)
    {
        return inventoryProxy.Create((Inventory)message);
    }

    public bool Rollback(IMessage message)
    {
        return inventoryProxy.Delete((Inventory)message);
    }
}

The code here will be identical to the OrderProxy. The only difference is that the type of proxy is Inventory and we will cast the IMessage to Inventory.

So now we have the command, that takes the receiver to execute the function. And then we have to have the invoker, and for the invoker, we are going to create an orchestrator.

Orchestrator class

namespace CommandPattern.Demo;

public interface IOrchestrator
{
    bool CreateOrder(Order order);
}

public class Orchestrator : IOrchestrator
{
    private readonly ICommand orderCommand;
    private readonly ICommand inventoryCommand;

    public Orchestrator(ICommand orderCommand, ICommand inventoryCommand)
    {
        this.orderCommand = orderCommand;
        this.inventoryCommand = inventoryCommand;
    }

    public bool CreateOrder(Order order)
    {
        if (orderCommand.Execute(order))
        {
            if (inventoryCommand.Execute(new Inventory(order.ProductName, order.Quantity)))
            {
                return true;
            }
            else
            {
                return orderCommand.Rollback(order);
            }
        }
        return false;
    }
}

The orchestrator class has a single method CreateOrder, which will take an order object as a parameter. And this is where we are going to use command instead of directly calling the proxies. That is how we will achieve the decoupling.

In the constructor of this class, we are going to take the ICommand of type Order and the ICommand of type Inventory.

And inside the CreateCommand method, we will first call Execute on the OrderCommand. And only if that is successful, we will call the Execute on the InventoryCommand.

But if the Execute call of InventoryCommand fails, we will call the Rollback on the OrderCommand.

So this is a simple example of a distributed transaction using a command pattern.

The client of the command pattern

Now finally we are going to create the client of the command pattern. And the client here is nothing but the API for executing the order.

And for the API we will use the minimal API construct in the program class.

app.MapPost("/api/order", ([FromBody] Order order, [FromServices] IOrchestrator orchestrator) => 
{ 
    return orchestrator.CreateOrder(order);
});

The API “api/order” will call the CreateOrder of the Orchestrator class. And the API will pass the Order object from the request body.

Next, I will add all the dependencies needed to the dependency injection container. With all the updates the Program class will look like below:

using CommandPattern.Demo;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddSingleton<IProxy<Order>, OrderProxy>();
builder.Services.AddSingleton<IProxy<Inventory>, InventoryProxy>();
builder.Services.AddSingleton<IOrchestrator>(x => new Orchestrator(
        new OrderCommand(x.GetService<IProxy<Order>>()),
        new InventoryCommand(x.GetService<IProxy<Inventory>>())
    ));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.MapPost("/api/order", ([FromBody] Order order, [FromServices] IOrchestrator orchestrator) => 
{ 
    return orchestrator.CreateOrder(order);
});

app.Run();

Running the command pattern implementation

After running the program, we can use the Swagger page to execute the API. And for the Order object, we will use the product name as Milk and quantity as 2 gallons.

When we execute, we can see the response body as true. And if we look into the console, we can see both print statements as expected in the output.

command pattern

Next, I will change the code in such a way as to mimic a failure during inventory creation. And to do that I will return false instead of true as a return for the Create method of the InventoryProxy.

And now if we run the application and execute the API, we will see it will roll back by deleting the transactions.

command pattern rollback

Conclusion

This is how we can use the command pattern to execute a distributed transaction and decouple the actual proxy from the orchestrator through the individual command classes. I think if we use the command pattern it just encapsulates and makes the code more readable in terms of execution and rollback. And it also gives one more layer of abstraction between the actual receiver, which is the proxy, and the invoker which is the orchestrator.

A Youtube video for this implementation is available here.