Command Pattern – How to Inject Dependencies into a Command Pattern

builder-patternccommand-patterndependency-injectiondesign-patterns

I am pretty new to programming languages and only have limited knowledge about design patterns, so I hope you can help me with the following problem:

I have an application that operates on a group of different services. One functionality of the application is, to provide an interface to the user to call every method of the available services. Therefore I want to use the Command pattern because it allows me to add new commands just by adding new classes and not by changing the existing code. The parameters for each service command are passed to the constructor.

Commands:

public interface ICommand {
    void Execute();
}

public abstract class Command<T> : ICommand {
    public T Service { get; set; }

    public abstract void Execute() { /* use service */ }
}

public class Command1 : Command<Service1> {
    T1 param1;
    ...

   public Command1(T1 param1, ...) { /* set parameters */ }

   public override void Execute() { /* call first service1 method */ }
}

...

public class Command2 : Command<Service2> {
    T2 param1;

    ...

   public override void Execute() { /* call first service2 method */ }
}

...

The advantage is, that the user can instantiate a group of commands without knowing the application's interface and execute them at a later time when the service was set. The problem is, that I don't know how I can elegantly inject the services.
The application is mainly responsible for starting and stopping the services and keeping an instance of each service in a central place.

Application:

public class Application {
    S1 Service1;
    S2 Service2,
    ...

    public void StartService(/* params */) { /* ... */ }
    public void StopService(/* params */) { /* ... */ }
    ...
}

Question:

So my question is how do I get the correct service inside a command?
I thought about using some kind of Dependency injection, Service locator or Builder pattern, but I never used these patterns and I am not sure what's the best solution in this case and how to implement it correctly.

Update:

Thanks to the comment of @Andy and @Anders it's probably the best solution to use a Command class for my parameters and a CommandHandler class for the logic. The advantage is, that I can instantiate a command handler inside the Application class and pass the correct service in the handler's constructor. Also, I can create a command outside the Application without knowing the service a pass this command to the Application for execution.
To map commands to the correct command handler I a CommmandBus as suggested by @Andy, but I have some trouble implementing the java example in C# because there is no template map like Map<Class<? extends CommandHandler<?>>, CommandHandler<? extends Command>> in C#.

So what's a clean solution to map a command to its handler in C#? I don't really like my solution below because I have to upcast the command.

My updated code:

public interface ICommand { }

public class ConcreteCommand : ICommand {
    public Type1 Param1 { get; set; }
    public Type2 Param2 { get; set; }
    /* some more parameters */
}

public interface ICommandHandler<in TCommand> {
    Task Handle(TCommand command);
}

public class ConcreteCommandHandler : ICommandHandler<ConcreteCommand> {
    private readonly S1 service;

    public ConcreteCommandHandler(S1 service) {
        this.service = service;
    }

    public Task Handle(ConcreteCommand command) {
        return service.DoSomething(command.Param1, ...);
    }
}


public class CommandBus {
    /* BAD: Return type of command handler hardcoded */
    private readonly IDictionary<Type, Func<ICommand, Task>> handlerMap = new Dictionary<Type, Func<ICommand, Task>>();

        public void RegisterCommandHandler<TCommand>(ICommandHandler<TCommand> handler) where TCommand: ICommand
        {
            Type commandType = typeof(TCommand);

            if (handlerMap.ContainsKey(commandType))
                throw new HandlerAlreadyRegisteredException(commandType);

            /* BAD: Narrowing cast */
            handlerMap.Add(commandType, command => handler.Handle((TCommand) command));
        }

        public Task Dispatch<TCommand>(TCommand command) where TCommand : ICommand
        {
            Type commandType = typeof(TCommand);

            if (!handlerMap.TryGetValue(commandType, out Func<ICommand, Task> handler))
                throw new HandlerNotRegisteredException(commandType);

            return handler(command);
        }
}


public class Application {
    private CommandBus bus;
    private S1 service1;
    private S2 service2;
    ...

    private void InitializeBus() {
        bus.RegisterCommandHandler(new ConcreteCommandHandler(service1))
        ...
    }

    public void ExecuteCommand<TCommand>(TCommand command) where TCommand : ICommand {
        bus.Dispatch(command);
    }

    ...
}

Best Answer

Before answering your question, one should firstly know the goal developers are trying to achieve with the command pattern in the first place. More often than not, the pattern's purpose is to decouple modules from each other and to provide a sensible abstraction for execution of intents (commands) to the system.

In order to understand how commands fit into an application, let's integrate the command pattern by refactoring a hypothetical application which allows users to be registered. I apologise for the Java language, I haven't programmed in C# for a long time. In this very simple application we have a service and a controller:

class UserService {
    private UserRepository userRepository;
    private PasswordHashService passwordHashService;

    public User registerUser(
        String firstName,
        String lastName,
        String email,
        String passwordInPlainText
    ) {
        User userToBeRegistered = new User();
        userToBeRegistered.setId(userRepository.retrieveNewId());
        userToBeRegistered.setFirstName(firstName);
        userToBeRegistered.setLastName(lastName);
        userToBeRegistered.setEmail(email);
        userToBeRegistered.setPassword(passwordHashService.hash(passwordInPlainText));

        userRepository.save(userToBeRegistered);

        return userToBeRegistered;
    }
}

class UserController {
    private UserService userService;

    public Response<User> registerUser(FormInput formInput) {
        return new Response<>(userService.registerUser(
            formInput.getString("first_name"),
            formInput.getString("last_name"),
            formInput.getString("email"),
            formInput.getString("password")
        ));
    }
}

On the service layer, there's a design issue. The method takes 4 string arguments in a very specific order. This couples the caller of the method to the service and refactoring the method by adding a new optional argument might could end up being difficult if you called the registerUser from multiple places.

In order to reduce the number of arguments of the service method, let's introduce a special DTO object, acting as a data messenger between the two layers, and for the same of convenience, name it RegisterUserCommand. The Object would have the following structure:

class RegisterUserCommand {
    private String firstName;
    private String lastName;
    private String email;
    private String passwordInPlainText

    // getters and setter are omitted
}

which will lead to design change of the service method, now looking like this:

public User registerUser(RegisterUserCommand command) {
    User userToBeRegistered = new User();
    userToBeRegistered.setId(userRepository.retrieveNewId());
    userToBeRegistered.setFirstName(command.getFirstName());
    userToBeRegistered.setLastName(command.getLastName());
    userToBeRegistered.setEmail(command.getEmail());
    userToBeRegistered.setPassword(passwordHashService.hash(
        command.getPasswordInPlainText()
    ));

    userRepository.save(userToBeRegistered);

    return userToBeRegistered;
}

and the controllers method would change to:

public Response<User> registerUser(FormInput formInput) {
    RegisterUserCommand command = new RegisterUserCommand();
    command.setFirstName(formInput.getString("first_name"));
    command.setLastName(formInput.getString("last_name"));
    command.setEmail(formInput.getString("email"));
    command.setPasswordInPlainText(formInput.getString("password"));

    return new Response<>(userService.registerUser(command));
}

This fixes the unnecessary argument positional coupling, making it easier to introduce optional arguments to the user registration method by simply adding a new attribute to the command objects. In places where you don't work with the optional argument, no changes are necessary at all, in other places you may utilise the newly added property.

However the current design still contains coupling between the controller and the service. In this case, I wouldn't say it's a huge problem but since we're discussing a full command pattern integration, we'll refactor the code a bit further.

Introduction of a command bus

Using the command pattern without having some variant of a command bus is pretty meaningless. But what is this command bus? In short, it's mostly a glorified service locator. Some advanced implementations of command buses allow you to use plugins, middle-wares, effectively giving you the ability to extend the functionality of the command bus execution process without having to know it's internals.

Command busses generally consist of two main parts:

  1. service registry (some internal collection where instances of services live),
  2. command to service mapping (configuration telling which command type should be processed by which service).

Both of those two parts need to be configured by you. The 1. part requires you to somehow provide service instances to the command bus, and the 2. part requires you defining the mapping.

A very basic command bus implementation (really basic, no support for plugins etc.) could look like this:

interface Command {
}

interface CommandHandler<T extends Command> {
    Object execute(T command);
}

class CommandBus {
    private Map<Class<? extends CommandHandler<?>>, CommandHandler<? extends Command>> commandHandlers;
    private Map<Class<? extends Command>, Class<? extends CommandHandler<?>>> commandToHandlerConfig;

    public CommandBus() {
        commandHandlers = new HashMap<>();
        commandToHandlerConfig = new HashMap<>();
    }

    public void registerCommandHandler(CommandHandler<? extends Command> handler) {
        Class<CommandHandler<?>> clazz = (Class<CommandHandler<?>>) handler.getClass();

        if (commandHandlers.containsKey(clazz)) {
            throw new RuntimeException("The command handler " + clazz + " is already registered.");
        }

        commandHandlers.put(clazz, handler);
    }

    public void registerCommandToCommandHandler(
            Class<? extends Command> command,
            Class<? extends CommandHandler<?>> handler
    ) {
        if (!commandHandlers.containsKey(handler)) {
            throw new RuntimeException("The command handler " + handler + " is not registered.");
        }

        commandToHandlerConfig.put(command, handler);
    }

    public <T extends Command, U> U dispatch(T command, Class<U> resultClass) {
        Class<?> commandClass = command.getClass();

        if (!commandToHandlerConfig.containsKey(commandClass)) {
            throw new RuntimeException(
                    "The command " + commandClass + " could not be executed, no handler is configured."
            );
        }

        Class<? extends CommandHandler<?>> handlerClass = commandToHandlerConfig.get(commandClass);
        CommandHandler<? super Command> handler = (CommandHandler<? super Command>) commandHandlers.get(handlerClass);

        return resultClass.cast(handler.execute(command));
    }
}

This command bus allows you to register a command handler to a registry (fulfilling the 1. part) and to provide mapping for a given command to a certain handler (fulfilling the 2. part). Besides that it allows you to execute a command with an expected result class.

As a part of implementation, you may have noticed that that we've also introduced two interfaces, Command and CommandHandler. Those are necessary in compiled languages, but could be theoretically omitted in dynamic languages such as PHP or Python (as long as you'd follow certain guidelines defined by the command bus' implementation - mainly the method of the execution method of a handler).

Simple integration of our command bus

First we have to make our RegisterUserCommand implement the Command interface:

class RegisterUserCommand implements Command {
    // [...] the rest remains the same
}

then we make the UserService implement the CommandHandler interface, i.e. we need to add an appropriate execute method implementation. For the sake of simplicity, let's change our current registerUser method to the execute method.

class UserService implements CommandHandler<RegisterUserCommand> {
    // [...] the rest remains the same

    public Object execute(RegisterUserCommand command) {
        // [...] the rest remains the same
    }
}

These changes have been basically made so that the command and the service may be used in our command bus context. Right now you may have noticed a caveat. A class may only implement a generic interface only once, which forces you to basically have one command handler for each command. While you might find this troublesome at the beginning, in the long run this is actually very nice, since you'll end up with many tiny command handlers, each having only a single responsibility - handling a specific command instance.

A very basic integration of our command bus on the controller level could then look like the following (without actually making too much sense at the moment):

public Response<User> registerUser(FormInput formInput) {
    RegisterUserCommand command = new RegisterUserCommand();
    command.setFirstName(formInput.getString("first_name"));
    command.setLastName(formInput.getString("last_name"));
    command.setEmail(formInput.getString("email"));
    command.setPasswordInPlainText(formInput.getString("password"));

    CommandBus commandBus = new CommandBus();
    commandBus.registerCommandHandler(userService);
    commandBus.registerCommandToCommandHandler(
        RegisterUserCommand.class,
        userService.getClass()
    );

    return new Response<>(commandBus.dispatch(command, User.class));
}

Here we have manually created an instance of the command bus, registered a command handler and a command to handler mapping, basically forcing the command bus acting as a proxy. In reality, in big application there would usually be at most a few instances of a few separate command busses (or perhaps even only a single instance), which would come pre-configured, already containing all registered handlers and mappings, and such configured command bus would be injected, turning your controller into the following:

class UserController {
    private CommandBus commandBus;

    public Response<User> registerUser(FormInput formInput) {
        RegisterUserCommand command = new RegisterUserCommand();
        command.setFirstName(formInput.getString("first_name"));
        command.setLastName(formInput.getString("last_name"));
        command.setEmail(formInput.getString("email"));
        command.setPasswordInPlainText(formInput.getString("password"));

        return new Response<>(commandBus.dispatch(command, User.class));
    }
}

Thanks to this, you are no longer coupled to the UserService and the mapping is done on an infrastructure level (through configuration). This can be beneficial as it reduces number of dependencies of services, should a single service/controller/command handler invoke multiple commands.

The configuration could be e.g. a YAML, looking e.g. like this:

- CommandBus:
  - Handlers:
    - RegisterUser: UserService
    - SomeOtherCommand: SomeOtherHandler

The CommandBus would obviously need to provide a mechanism to correctly parse it.

Some advantages of a command bus

  • Reduced coupling
  • Seamless addition of plugins (when using a decent command bus implementation)
  • Potential reduction of injected dependencies
  • Leads to more cohesive and composable code (many small cohesive command handlers)
  • Theoretically prepares project for different type of communication between layer, e.g. through an enterprise service bus

Some disadvantages of a command bus

  • Complexity overhead (mapping through layers is done in configuration and is less visible)
  • Each command has go through command bus proxy
  • When going for a ready solution of a command bus, you lock yourself to the vendor

Other potential (dis)advantages may come up during discussion of command bus integration into your own project. Whether the approach to use a command bus is fitting for a given project or not depends on many variables and has to be talked through with the system's architect.

Related Topic