Replacing If-Else With Commands and Handlers
By: Nicklas Millard
Tags: Dotnet Core, Programming, Software Development, Software Engineering, Technology

if-else is, at its very core, not bad. It’s merely just a hammer and nail situation we got going. In programming 101, you’ll learn conditional statements, and lots of developers never mature their practices beyond that.

But, if-else and switch are often not ideal. Better approaches, such as polymorphic execution and dictionaries, are typically neglected.

We want to avoid traditional, conditional branches.

I’ve written an article proposing a way of replacing conditional branching with polymorphic execution. To get some context, I’ll briefly repeat some of the earlier article examples before we deep dive into commands and handlers.

Here’s the example of what we’d like to avoid: Nasty, difficult to extend, branching on a discrete value.

Complicated, headache-inducing branching

Besides the freakish use of if-elseif-else , the main issue is you need to add a branch for every new update reason. A clear violation of the Open/Closed and Single Responsibility principles.

Each branch can basically be converted to its own command and corresponding handlers.

Let’s take a look at how that’s possible.

Using commands and handlers to simplify your application.

📝 GitHub Repo

I won’t preach the theory about what commands, queries, and handlers are. There are plenty of resources on this topic. Instead, I’ve composed a brief list of what the advantages may be.

Testing becomes tremendously easier.

You don’t need to update existing tests to account for new features. If a command requires additional processing, you create another handler, which you test in isolation. Multiple handlers can handle one command.

As you’ve likely already noted, dispatching one command may invoke one or more handlers. In this way, you can add new functionality without touching existing code. Stupid, Simple Classes.

A command is a bag of properties with no setters. Not much can go wrong here. Likewise, for a handler, it’s a class with only one public method. Controller actions adhere to a Request-Delegate-Response pattern.

They’ll contain no business or persistence logic whatsoever.

If you’re practicing event-storming, I’m sure you’re already completely aligned with why commands and handlers are awesome.

I ended the previously mentioned article, hinting how you can use dynamic command dispatching to eliminate unnecessary branching. And now, you’ll see one way of implementing commands and handlers.

Finally, some code!

To follow this code, let me very quickly summarize to you what we want to achieve, in a general sense.

We want to say, “Okay, something needs to happen. Here are the values. I don’t care who handles it, just let me know when it’s done”.

There are three acceptance criteria we’ll need to fulfill:

A command can be dispatched without the caller knowing the concrete handlers. Every handler matching the command needs to be executed. New commands or processing steps do not require you to modify existing code.

We’ll start from the outermost layer and work our way in.

Departing from the controller’s perspective, it’s irrelevant to know about concrete handlers or even interfaces. The action should only be focused on data.

For this, we want the controller action to be just as simple as this below.

Update email endpoint

You should get the gist it, even though this is C# aspnetcore. Simply put, it’s a controller action — the endpoint and its implementation.

I know what you’re thinking: “where’s the error handling?!”. Don’t worry. You’re right. It should be there. But for brevity, I’ve left that part out so we can focus on the concept of dispatching commands.

The controller has a dependency on a CommandDispatcher . We’ll get to that class later. The dispatcher class has a single method DispatchAsync(command) . That’s all you’ll need to know for now.

This allows our controller to only care about validating the correctness of the data it receives and sending commands. How data is handled after dispatching is entirely irrelevant for the controller.

Each “update reason” requires its own endpoint, with its own data shape — i.e., the command to send.

At this point, implementing new features such as “update username” is as simple as creating a new endpoint and send the command.

Update username endpoint

When using this approach, creating endpoints becomes enormously trivial. And that’s a good thing.

Our endpoint is essentially done now.

So, let’s move on.

Commands and handlers are where all the business logic is situated.

With commands, you essentially only want to care about two things: immutability and data correctness.

They are just plain, old regular classes. Nothing fancy at all. Take a look at this ChangeEmailCommand .

Plain old command class

Obviously, this command class doesn’t do much. That’s the whole point. Its purpose is to be passed on to a handler.

Which brings us to the handler. Take a few to read through the code below. I’ll try to describe what’s going on after.

Simple, testable command handler.

First, we have an interface that all command handlers need to implement. The interface is important when you need dynamic type discovery. We’ll get to that in a minute.

Second, I’ve created a simple handler that knows how to deal with ChangeEmailCommand s. The generic parameter of ICommandHandlerAsync tells us, “this handler needs to be invoked whenever a ‘change email command’ is dispatched.”

Do you get a feeling of how d*mn testable this class is? That’s the whole point. It should be stupidly easy to test. The class is very focused. One method, one dependency.

If you’re used to “Service” classes, you know how crazy constructors sometimes get. This approach completely eliminates constructor bloat.

The dispatcher itself. Incredibly simple and robust.

You’ve already seen the dispatcher interface. Clean and simple. But let’s jog your memory once more.

Command dispatcher’s public interface

Before getting distracted with the implementation, let me just reiterate what we need to achieve with the CommandDispatcher .

We want to say, “here’s a command, go grab all the matching handlers and pass the command to each handler.”

This means, for each command class, we need a list of matching command handlers. In code, we can express this intent with a dictionary, where the key is the type of command, and the value is a list of handlers.

Take whatever time you need to read thru this. OOP people might find this super easy, while others won’t. I’ll describe what we got going, below the code.