An introduction to the Command Handler contravariant functor for object-oriented programmers.

This article is an instalment in an article series about contravariant functors. It assumes that you've read the introduction.

Asynchronous software architectures, such as those described in Enterprise Integration Patterns, often make good use of a pattern where Commands are (preferably immutable) Data Transfer Objects (DTOs) that are often placed on a persistent queue and later handled by a background process.

Even if you don't use asynchronous processing, separating command data from command handling can be beneficial for your software's granular architecture. In perhaps his most remarkable contribution to our book, Steven van Deursen describes how this pattern can greatly simplify how you deal with cross-cutting concerns.

Interface #

In DIPPP the interface is called ICommandService, but in this article I'll instead call it ICommandHandler. It's a generic interface with a single method:

public interface ICommandHandler<TCommand>
{
    void Execute(TCommand command);
}

The book explains how this interface enables you to gracefully handle cross-cutting concerns without any reflection magic. You can also peruse its example code base on GitHub. In this article, however, I'm using a fork of that code because I wanted to make the properties of contravariant functors stand out more clearly.

In the sample code base, an ASP.NET Controller delegates work to an injected ICommandHandler<AdjustInventory> called inventoryAdjuster.

[Route("inventory/adjustinventory")]
public ActionResult AdjustInventory(AdjustInventoryViewModel viewModel)
{
    if (!this.ModelState.IsValid)
    {
        return this.View(nameof(Index), this.Populate(viewModel));
    }
 
    AdjustInventory command = viewModel.Command;
 
    this.inventoryAdjuster.Execute(command);
 
    this.TempData["SuccessMessage"] = "Inventory successfully adjusted.";
 
    return this.RedirectToAction(nameof(HomeController.Index), "Home");
}

There's a single implementation of ICommandHandler<AdjustInventory>, which is a class called AdjustInventoryService:

public class AdjustInventoryService : ICommandHandler<AdjustInventory>
{
    private readonly IInventoryRepository repository;
 
    public AdjustInventoryService(IInventoryRepository repository)
    {
        if (repository == null)
            throw new ArgumentNullException(nameof(repository));
 
        this.repository = repository;
    }
 
    public void Execute(AdjustInventory command)
    {
        var productInventory = this.repository.GetByIdOrNull(command.ProductId)
            ?? new ProductInventory(command.ProductId);
 
        int quantityAdjustment = command.Quantity * (command.Decrease ? -1 : 1);
        productInventory = productInventory.AdjustQuantity(quantityAdjustment);
 
        if (productInventory.Quantity < 0)
            throw new InvalidOperationException("Can't decrease below 0.");
 
        this.repository.Save(productInventory);
    }
}

The Execute method first loads the inventory data from the database, calculates how to adjust it, and saves it. This is all fine and good object-oriented design, and my intent with the present article isn't to point fingers at it. My intent is only to demonstrate how the ICommandHandler interface gives rise to a contravariant functor.

I'm using this particular code base because it provides a good setting for a realistic example.

Towards Domain-Driven Design #

Consider these two lines of code from AdjustInventoryService:

int quantityAdjustment = command.Quantity * (command.Decrease ? -1 : 1);
productInventory = productInventory.AdjustQuantity(quantityAdjustment);

Doesn't that look like a case of Feature Envy? Doesn't this calculation belong better on another class? Which one? The AdjustInventory Command? That's one option, but in this style of architecture Commands are supposed to be dumb DTOs, so that may not be the best fit. ProductInventory? That may be more promising.

Before making that change, however, let's consider the current state of the class.

One of the changes I made in my fork of the code was to turn the ProductInventory class into an immutable Value Object, as recommended in DDD:

public sealed class ProductInventory
{
    public ProductInventory(Guid id) : this(id, 0)
    {
    }
 
    public ProductInventory(Guid idint quantity)
    {
        Id = id;
        Quantity = quantity;
    }
 
    public Guid Id { get; }
    public int Quantity { get; }
 
    public ProductInventory WithQuantity(int newQuantity)
    {
        return new ProductInventory(Id, newQuantity);
    }
 
    public ProductInventory AdjustQuantity(int adjustment)
    {
        return WithQuantity(Quantity + adjustment);
    }
 
    public override bool Equals(object obj)
    {
        return obj is ProductInventory inventory &&
               Id.Equals(inventory.Id) &&
               Quantity == inventory.Quantity;
    }
 
    public override int GetHashCode()
    {
        return HashCode.Combine(Id, Quantity);
    }
}

That looks like a lot of code, but keep in mind that typing isn't the bottleneck - and besides, most of that code was written by various Visual Studio Quick Actions.

Let's try to add a Handle method to ProductInventory:

public ProductInventory Handle(AdjustInventory command)
{
    var adjustment = command.Quantity * (command.Decrease ? -1 : 1);
    return AdjustQuantity(adjustment);
}

While AdjustInventoryService isn't too difficult to unit test, it still does require setting up and configuring some Test Doubles. The new method, on the other hand, is actually a pure function, which means that it's trivial to unit test:

[Theory]
[InlineData(0, false, 0, 0)]
[InlineData(0,  true, 0, 0)]
[InlineData(0, false, 1, 1)]
[InlineData(0, false, 2, 2)]
[InlineData(1, false, 1, 2)]
[InlineData(2, false, 3, 5)]
[InlineData(5,  true, 2, 3)]
[InlineData(5,  true, 5, 0)]
public void Handle(
    int initial,
    bool decrease,
    int adjustment,
    int expected)
{
    var sut = new ProductInventory(Guid.NewGuid(), initial);
 
    var command = new AdjustInventory
    {
        ProductId = sut.Id,
        Decrease = decrease,
        Quantity = adjustment
    };
    var actual = sut.Handle(command);
 
    Assert.Equal(sut.WithQuantity(expected), actual);
}

Now that the new function is available on ProductInventory, you can use it in AdjustInventoryService:

public void Execute(AdjustInventory command)
{
    var productInventory = this.repository.GetByIdOrNull(command.ProductId)
        ?? new ProductInventory(command.ProductId);
 
    productInventory = productInventory.Handle(command);
 
    if (productInventory.Quantity < 0)
        throw new InvalidOperationException("Can't decrease below 0.");
 
    this.repository.Save(productInventory);
}

The Execute method now delegates its central logic to ProductInventory.Handle.

Encapsulation #

If you consider the Execute method in its current incarnation, you may wonder why it checks whether the Quantity is negative. Shouldn't that be the responsibility of ProductInventory? Why do we even allow ProductInventory to enter an invalid state?

This breaks encapsulation. Encapsulation is one of the most misunderstood concepts in programming, but as I explain in my PluralSight course, as a minimum requirement, an object should not allow itself to be put into an invalid state.

How to better encapsulate ProductInventory? Add a Guard Clause to the constructor:

public ProductInventory(Guid idint quantity)
{
    if (quantity < 0)
        throw new ArgumentOutOfRangeException(
            nameof(quantity),
            "Negative quantity not allowed.");
 
    Id = id;
    Quantity = quantity;
}

Again, such behaviour is trivial to drive with a unit test:

[Theory]
[InlineData( -1)]
[InlineData( -2)]
[InlineData(-19)]
public void SetNegativeQuantity(int negative)
{
    var id = Guid.NewGuid();
    Action action = () => new ProductInventory(id, negative);
    Assert.Throws<ArgumentOutOfRangeException>(action);
}

With those changes in place, AdjustInventoryService becomes even simpler:

public void Execute(AdjustInventory command)
{
    var productInventory = this.repository.GetByIdOrNull(command.ProductId)
        ?? new ProductInventory(command.ProductId);
 
    productInventory = productInventory.Handle(command);
 
    this.repository.Save(productInventory);
}

Perhaps even so simple that the class begins to seem unwarranted.

Sandwich #

It's just a database Query, a single pure function call, and another database Command. In fact, it looks a lot like an impureim sandwich:

public void Execute(AdjustInventory command)
{
    var productInventory = this.repository.GetByIdOrNull(command.ProductId)
        ?? new ProductInventory(command.ProductId);
 
    productInventory = productInventory.Handle(command);
 
    this.repository.Save(productInventory);
}

In fact, it'd probably be more appropriate to move the null-handling closer to the other referentially transparent code:

public void Execute(AdjustInventory command)
{
    var productInventory = this.repository.GetByIdOrNull(command.ProductId);
    productInventory =
        (productInventory ?? new ProductInventory(command.ProductId)).Handle(command);
    this.repository.Save(productInventory);
}

Why do we need the AdjustInventoryService class, again?

Can't we move those three lines of code to the Controller? We could, but that might make testing the above AdjustInventory Controller action more difficult. After all, at the moment, the Controller has an injected ICommandHandler<AdjustInventory>, which is easy to replace with a Test Double.

If only we could somehow compose an ICommandHandler<AdjustInventory> from the above sandwich without having to define a class...

Contravariant functor #

Fortunately, an interface like ICommandHandler<T> gives rise to a contravariant functor. This will enable you to compose an ICommandHandler<AdjustInventory> object from the above constituent parts.

In order to enable contravariant mapping, you must add a ContraMap method:

public static ICommandHandler<T1> ContraMap<TT1>(
    this ICommandHandler<T> source,
    Func<T1, T> selector)
{
    Action<T1> action = x => source.Execute(selector(x));
    return new DelegatingCommandHandler<T1>(action);
}

Notice that, as explained in the overview article, in order to map from an ICommandHandler<T> to an ICommandHandler<T1>, the selector has to go the other way: from T1 to T. How this is possible will become more apparent with an example, which will follow later in the article.

The ContraMap method uses a DelegatingCommandHandler that wraps any Action<T>:

public class DelegatingCommandHandler<T> : ICommandHandler<T>
{
    private readonly Action<T> action;
 
    public DelegatingCommandHandler(Action<T> action)
    {
        this.action = action;
    }
 
    public void Execute(T command)
    {
        action(command);
    }
}

If you're now wondering whether Action<T> itself gives rise to a contravariant functor, then yes it does.

Identity law #

A ContraMap method with the right signature isn't enough to be a contravariant functor. It must also obey the contravariant functor laws. As usual, it's proper computer-science work to actually prove this, but you can write some tests to demonstrate the identity law for the ICommandHandler<T> interface. In this article, you'll see parametrised tests written with xUnit.net. First, the identity law:

[Theory]
[InlineData("foo")]
[InlineData("bar")]
[InlineData("baz")]
[InlineData("qux")]
[InlineData("quux")]
[InlineData("quuz")]
[InlineData("corge")]
[InlineData("grault")]
[InlineData("garply")]
public void IdentityLaw(string input)
{
    var observations = new List<string>();
    ICommandHandler<stringsut = new DelegatingCommandHandler<string>(observations.Add);
 
    T id<T>(T x) => x;
    ICommandHandler<stringprojection = sut.ContraMap<stringstring>(id);
 
    // Run both handlers
    sut.Execute(input);
    projection.Execute(input);
    Assert.Equal(2, observations.Count);
    Assert.Single(observations.Distinct());
}

In order to observe that the two handlers have identical behaviours, the test has to Execute both of them to verify that both observations are the same.

All test cases pass.

Composition law #

Like the above example, you can also write a parametrised test that demonstrates that ContraMap obeys the composition law for contravariant functors:

[Theory]
[InlineData("foo")]
[InlineData("bar")]
[InlineData("baz")]
[InlineData("qux")]
[InlineData("quux")]
[InlineData("quuz")]
[InlineData("corge")]
[InlineData("grault")]
[InlineData("garply")]
public void CompositionLaw(string input)
{
    var observations = new List<TimeSpan>();
    ICommandHandler<TimeSpan> sut = new DelegatingCommandHandler<TimeSpan>(observations.Add);
 
    Func<stringintf = s => s.Length;
    Func<int, TimeSpan> g = i => TimeSpan.FromDays(i);
    ICommandHandler<stringprojection1 = sut.ContraMap((string s) => g(f(s)));
    ICommandHandler<stringprojection2 = sut.ContraMap(g).ContraMap(f);
 
    // Run both handlers
    projection1.Execute(input);
    projection2.Execute(input);
    Assert.Equal(2, observations.Count);
    Assert.Single(observations.Distinct());
}

This test defines two local functions, f and g. Once more, you can't directly compare methods for equality, so instead you have to Execute them to verify that they produce the same observable effect.

They do.

Composed inventory adjustment handler #

We can now return to the inventory adjustment example. You may recall that the Controller would Execute a command on an injected ICommandHandler<AdjustInventory>:

this.inventoryAdjuster.Execute(command);

As a first step, we can attempt to compose inventoryAdjuster on the fly:

ICommandHandler<AdjustInventory> inventoryAdjuster =
    new DelegatingCommandHandler<ProductInventory>(repository.Save)
        .ContraMap((ProductInventory inv) =>
            (inv ?? new ProductInventory(command.ProductId)).Handle(command))
        .ContraMap((AdjustInventory cmd) =>
            repository.GetByIdOrNull(cmd.ProductId));
inventoryAdjuster.Execute(command);

Contra-mapping is hard to get one's head around, and to make matters worse, you have to read it from the bottom towards the top to understand what it does. It really is contrarian.

How do you arrive at something like this?

You start by looking at what you have. The Controller may already have an injected repository with various methods. repository.Save, for example, has this signature:

void Save(ProductInventory productInventory);

Since it has a void return type, you can treat repository.Save as an Action<ProductInventory>. Wrap it in a DelegatingCommandHandler and you have an ICommandHandler<ProductInventory>:

ICommandHandler<ProductInventory> inventoryAdjuster =
    new DelegatingCommandHandler<ProductInventory>(repository.Save);

That's not what you need, though. You need an ICommandHandler<AdjustInventory>. How do you get closer to that?

You already know from the AdjustInventoryService class that you can use a pure function as the core of the impureim sandwich. Try that and see what it gives you:

ICommandHandler<ProductInventory> inventoryAdjuster =
    new DelegatingCommandHandler<ProductInventory>(repository.Save)
        .ContraMap((ProductInventory inv) =>
            (inv ?? new ProductInventory(command.ProductId)).Handle(command));

That doesn't change the type of the handler, but implements the desired functionality.

You have an ICommandHandler<ProductInventory> that you need to somehow map to an ICommandHandler<AdjustInventory>. How do you do that?

By supplying a function that goes the other way: from AdjustInventory to ProductInventory. Does such a method exist? Yes, it does, on the repository:

ProductInventory GetByIdOrNull(Guid id);

Or, close enough. While AdjustInventory is not a Guid, it comes with a Guid:

ICommandHandler<AdjustInventory> inventoryAdjuster =
    new DelegatingCommandHandler<ProductInventory>(repository.Save)
        .ContraMap((ProductInventory inv) =>
            (inv ?? new ProductInventory(command.ProductId)).Handle(command))
        .ContraMap((AdjustInventory cmd) =>
            repository.GetByIdOrNull(cmd.ProductId));

That's cool, but unfortunately, this composition cheats. It closes over command, which is a run-time variable only available inside the AdjustInventory Controller action.

If we're allowed to compose the Command Handler inside the AdjustInventory method, we might as well just have written:

var inv = repository.GetByIdOrNull(command.ProductId);
inv = (inv ?? new ProductInventory(command.ProductId)).Handle(command);
repository.Save(inv);

This is clearly much simpler, so why don't we do that?

In this particular example, that's probably a better idea overall, but I'm trying to explain what is possible with contravariant functors. The goal here is to decouple the caller (the Controller) from the handler. We want to be able to define the handler outside of the Controller action.

That's what the AdjustInventory class does, but can we leverage the contravariant functor to compose an ICommandHandler<AdjustInventory> without adding a new class?

Composition without closures #

The use of a closure in the above composition is what disqualifies it. Is it possible to compose an ICommandHandler<AdjustInventory> when the command object is unavailable to close over?

Yes, but it isn't pretty:

ICommandHandler<AdjustInventory> inventoryAdjuster =
    new DelegatingCommandHandler<ProductInventory>(repository.Save)
        .ContraMap((ValueTuple<AdjustInventory, ProductInventory> t) =>
            (t.Item2 ?? new ProductInventory(t.Item1.ProductId)).Handle(t.Item1))
        .ContraMap((AdjustInventory cmd) =>
            (cmd, repository.GetByIdOrNull(cmd.ProductId)));

You can let the composing function return a tuple of the original input value and the projected value. That's what the lowest ContraMap does. This means that the upper ContraMap receives this tuple to map. Not pretty, but possible.

I never said that this was the best way to address some of the concerns I've hinted at in this article. The purpose of the article was mainly to give you a sense of what a contravariant functor can do.

Action as a contravariant functor #

Wrapping an Action<T> in a DelegatingCommandHandler isn't necessary in order to form the contravariant functor. I only used the ICommandHandler interface as an object-oriented-friendly introduction to the example. In fact, any Action<T> gives rise to a contravariant functor with this ContraMap function:

public static Action<T1> ContraMap<TT1>(this Action<T> source, Func<T1, T> selector)
{
    return x => source(selector(x));
}

As you can tell, the function being returned is similar to the lambda expression used to implement ContraMap for ICommandHandler<T>.

This turns out to make little difference in the context of the examples shown here, so I'm not going to tire you with more example code.

Conclusion #

Any generic polymorphic interface or abstract method with a void return type gives rise to a contravariant functor. This includes the ICommandHandler<T> (originally ICommandService<T>) interface, but also another interface discussed in DIPPP: IEventHandler<TEvent>.

The utility of this insight may not be immediately apparent. Contrary to its built-in support for functors, C# doesn't have any language features that light up if you implement a ContraMap function. Even in Haskell where the Contravariant functor is available in the base library, I can't recall having ever used it.

Still, even if not a practical insight, the ubiquitous presence of contravariant functors in everyday programming 'building blocks' tells us something profound about the fabric of programming abstraction and polymorphism.

Next: The Specification contravariant functor.



Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

Published

Monday, 06 September 2021 05:46:00 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Monday, 06 September 2021 05:46:00 UTC