Command Bus Pattern

In this article, we’ll explore the “Command Bus” pattern, to help us get the command (write) part right.

The Command Pattern was one of the behavioral patterns described by The Gang of Four as a way for two objects to communicate.

Command bus is a part of CQRS

To complicate things a little, the pattern has evolved with alternative designs. Architectural Patterns such as CQS (Command-query Separation) and CQRS (Command Query Responsibility Segregation) also use Commands, however in their context – a Command is simply a message.

Terminology

A command is an object that signals an intent, like RegisterUser. Commands are passed to the command bus. Command bus will pass the command to a handler. The handler will perform the required actions, like registering users.

Now, let's deep dive into the specifics.

What is command?

Commands should contain all necessary information to be executed. They are basically Data Transfer Objects(DTO's). Because commands encapsulate all the information needed to execute, they can be handled synchronously or asynchronously.

Command Bus Implementation for NodeJS

First you need to install via;

NPM

npm install simple-command-bus

YARN

yarn add simple-command-bus

Now, we are going to create the Command first. It needs to contain all the input parameters.

let’s look at a sample command that encapsulates our input:

const { Command } = require("simple-command-bus");

class CreateUserCommand extends Command {
 
  constructor(userName, email, password) {
    super();
    this.userName = userName;
    this.email = email;
    this.password = password;
  }
}

module.exports = CreateUserCommand;

So, how are these commands executed?

Command Handlers

Commands are handled by command handlers. A command must be handled by exactly one handler. In this sense, it differs from event, where we don’t care how many handlers (listeners) are involved.

An example:


const userService = require("../Domain/Services/UserService")

class CreateUserHandler {
  async handle(command) {
    return await userService.newUser(command)
  }
}

module.exports = CreateUserHandler

Command handler dedicated to a single command, does the actual logic.

Handlers interpret the intent of a specific Command and perform the expected behavior. They have a 1:1 relationship with Commands – meaning that for each Command, there is only ever one Handler.

All the Command handler should be gathered to the Handlers folder  in index.js file

Take a look of an example:

const {
  CommandHandlerMiddleware,
  ClassNameExtractor,
  InMemoryLocator,
  HandleInflector
} = require("simple-command-bus")


const CreateUserHandler = require("./UserHandler/CreateUserHandler")

const commandHandlerMiddleware = new CommandHandlerMiddleware(
  new ClassNameExtractor(),
  new InMemoryLocator({
    CreateUserHandler: new CreateUserHandler()
  }),
  new HandleInflector()
)

module.exports = commandHandlerMiddleware

Command Bus

The command bus matches commands to handlers. This matching can be done automatically by some kind of naming convention.

  • We have a Command Bus that calls the appropriate Command Handle for the given Command.
  • We can have a Command Bus that wraps the above one in a database transaction.
  • We can have a Command Bus that wraps the above one to logs all incoming commands.

When a command is dispatched, the bus locates the handler and calls the handle method.

The interface of the Command Bus might look like this:

const { CommandBus, LoggerMiddleware } = require("simple-command-bus");
const commandHandlerMiddleware = require("../Command/Handlers");

const CreateUserCommand = require("../Command/UserCommand/CreateUserCommand");

const commandBus = new CommandBus([
  new LoggerMiddleware(console),
  commandHandlerMiddleware
]);

const registerUser = async (req, res) => {
  const { userName, email, password } = req.body;
  try {
const createUserCommand = new CreateUserCommand(userName,email,password);
    const user = await commandBus.handle(createUserCommand);
    return res.redirect("/profile");
};

Conclusion

With the Command bus pattern, we have reduced our controller to the following responsibilities:

  • Create a Command by extracting input parameters from the request
  • Create a Response by using the Command handler returned value (via the Command Bus)

We might realize that ditching the Command Bus and keeping the Command Handler and the Command can still be beneficial. We also might realize that Commands don’t solve our “read” logic...