Published on

Psst, wanna solid options?

Authors

What's up, boss?

Imagine that we were challenged by our stakeholders to provide a software solution for ordering process.

Pretty standard problem in some areas of human activity, one might say.

Of course, we went through an Event Storming session, identified all the events, established commands - and we understand the entire process.

Ah, understanding.

Nothing is better than understanding, even without working software.

Simply, we nailed it.

Let's add confirming order to our system.

It's pretty straightforward.

To confirm an order we need to get order identifier and partner details: id and email.

It is very important that partner identifier has specific structure and email - is just an email.

When we verified with success that those details are correct, we can retrieve the order and confirm it.

After that, we can safely send order confirmation email.

And, it's all.

But hey, we are not paid for mere talking!

Talking is fun, but coding is funnier, isn't it?

I say command, you say handler

Of course, we are using CQRS.

Let's imagine we have the following command and its symmetric handler:

public record ConfirmOrderCommand(
    string OrderId,
    string PartnerId,
    string PartnerEmail
) : ICommand;

public class ConfirmOrderCommandHandler : ICommandHandler<ConfirmOrderCommand>
{
    private IOrderRepository _orderRepository;
    private ISendEmail _emailSender;
    private IValidator<ConfirmOrderCommand> _confirmOrderCommandValidator;

    public ConfirmOrderCommandHandler(
        IOrderRepository orderRepository,
        ISendEmail emailSender,
        IValidator<ConfirmOrderCommand> _confirmOrderCommandValidator
    )
    {
        _orderRepository = orderRepository;
        _emailSender = emailSender;
        _confirmOrderCommandValidator = confirmOrderCommandValidator;
    }
    
    public async Task Handle(ConfirmOrderCommand command)
    {
        ValidationResult result = 
            await _confirmOrderCommandValidator.ValidateAsync(command);
        if(!result.IsValid)
        {
            throw new FailedValidationOf<ConfirmOrderCommand>(result.Errors);
        }

        Order order = await _orderRepository.Get(command.OrderId);
        order.Confirm();
        await _orderRepository.Save(order);
        await _emailSender.Send(new OrderConfirmationEmail(command.OrderId, command.PartnerId, command.PartnerEmail));
    }
}

Looks like a very solid piece of code.

Probably most of us, including you, dear Reader, saw this kind of lines, either in tutorials on in your daily codebase.

Nothing fancy, isn't it?

To complete the picture, here are some "technical" interfaces that someone could wonder about:

public interface ICommand {}
public interface ICommandHandler<TCommand> where TCommand : ICommand
{
    Task Handle(TCommand command);
}

public interface ValidationResult
{
    bool IsValid { get; }
    IEnumerable<string> Errors { get; }
}

public interface IValidator<T>
{
    Task<ValidationResult> ValidateAsync(T input);
}

public class FailedValidationOf<TCommand> : Exception where TCommand : ICommand
{
    public FailedValidationOf(IEnumerable<string> errors) : base("Here's some exception aggregating all validation errors")
    {
    }
}

Now everything should be crystal clear.

Probably hundreds, if not thousands, or even hundreds of thousands of handlers of such structure were written and make money (hopefully) each day.

Ah, yes, structure.

Let's try to work on the structure but keep behavior the same.

Sounds like a goal of a process called refactoring, isn't it?

For a while, let's imagine we put our curiosity to the highest levels and try to see what concepts we have in our little handler. (concepts? You might be interested in From concepts to architecture)

Validation - valid or invalid idea?

Validation looks like a solved problem and probably is.

But validation, especially in IT or programming tribes, has a very special meaning.

In real life, when humans deal with "invalid" information, they actually don't use any kind of statuses or boolean flags.

Code-wise, those are pretty standard and often happening constructs - what's more, they are useful too!

But can bool be considered harmful? I mean on the conceptual plane.

When we actually try to inspect ideas, explore them and understand how they work.

Let's try to apply this kind of thinking on our tiny example - will it change the structure?

Ok, back to the code.

Our handler - it accepts ConfirmOrderCommand which later requires validation - this responsibility got assigned to IValidator.

IValidator looks like a pretty technical construct.

I know we are pretty familiar with it and it does not look suspiciously.

"It does the job", one might say.

Yes, that's true - no doubts.

Again, let's enter this playful mode.

Of course, none of the stakeholders told us anything about IValidator but as DDD experts we still need to be pragmatic - and require our code (and handlers) doing the job.

Always...Sometimes valid domain model

There's a concept of "always valid domain model" - it basically means that when a thing gets created it must be in correct, coherent state so that others, when collaborating with it, won't act and play defensively.

In our scenario its quite opposite.

From the command handler's perspective, the responsibility for ensuring that its dealing with valid model remains in its hands.

Additionally, the concept of validation is imperatively stated inside this handler.

Could we act differently?

Imagine we talk to our command handler (YES! Aren't you talking to your code? If your curious, please look at Conversation-Driven Design):

Discussion
Us:Hey!
ConfirmOrderCommandHandler:Hey, what's up?
Us:Why are you checking that incoming command is valid?
ConfirmOrderCommandHandler:You know, all those commands these days...They don't communicate if they are valid - we, poor handlers, need to do this duty which might not be in our hands, to be honest...

What if, just if, command handlers didn't want to do this job?

Should we directly express in the code the concept of valid domain model?

Of course, it might incurr The cost of modeling, but hey, we are playing right now.

One might also say "Domain model? Damian, it's just validation!".

Fellow human beings know the idea of information integrity.

Unspoken ideas are as good as spoken ones.

Expressing them explicitly might make our thinking much clearer too!

Self validating concept?

Ok, let's do a silly move.

Let's make our command validating itself.

public record ConfirmOrderCommand(
    string OrderId,
    string PartnerId,
    string PartnerEmail
) : ICommand
{
    public Task ValidateUsing(IValidator<ConfirmOrderCommand> validator)
    {
        ValidationResult result = 
            await _confirmOrderCommandValidator.ValidateAsync(command);
        if(!result.IsValid)
        {
            throw new FailedValidationOf<ConfirmOrderCommand>(result.Errors);
        }
    } 
}

Then our handler gets a bit leaner.

public class ConfirmOrderCommandHandler : ICommandHandler<ConfirmOrderCommand>
{
    private IOrderRepository _orderRepository;
    private ISendEmail _emailSender;
    private IValidator<ConfirmOrderCommand> _confirmOrderCommandValidator;

    public ConfirmOrderCommandHandler(
        IOrderRepository orderRepository,
        ISendEmail emailSender,
        IValidator<ConfirmOrderCommand> _confirmOrderCommandValidator
    )
    {
        _orderRepository = orderRepository;
        _emailSender = emailSender;
        _confirmOrderCommandValidator = confirmOrderCommandValidator;
    }
    
    public async Task Handle(ConfirmOrderCommand command)
    {
        await command.ValidateUsing(_confirmOrderCommandValidator);

        Order order = await _orderRepository.Get(command.OrderId);
        order.Confirm();
        await _orderRepository.Save(order);
        await _emailSender.Send(new OrderConfirmationEmail(command.OrderId, command.PartnerId, command.PartnerEmail));
    }
}

Now, there's no if.

At least from the command handler perspective.

Still, it needs to tell ConfirmOrderCommand to validate itself.

Some might say that no one, especially nowadays, complicates code that much.

In the previous version, everything was visible and explicit - now, it's hidden, convoluted and nothing is straightforward.

Ok, let's try to put emotions aside and ask ourselves - what attributes does our alternative yield?

From the command handler perspective, less code to inspect and browse (inside).

In case of testing this handler, nothing really - still we need to control the IValidator.

Cognitively, current alternative abstracted away some details - which might be viewed as a nice thing (I like abstracting the details, what about you, dear Reader? Btw. you might be interested in The ambiguity of abstraction)

What other attributes are conjured through using this solution, dear Reader?

Could we do something else?

Create, not validate?

Ok, let's do some blasphemy here.

public record ConfirmOrderCommand(
    string OrderId,
    string PartnerId,
    string PartnerEmail
) : ICommand
{
    public Task<ConfirmOrderCommand> ValidateUsing(IValidator<ConfirmOrderCommand> validator)
    {
        ValidationResult result = await _confirmOrderCommandValidator.ValidateAsync(command);
        if(!result.IsValid)
        {
            throw new FailedValidationOf<ConfirmOrderCommand>(result.Errors);
        }

        return this;
    } 
}

Our ConfirmOrderCommand, after validating itself, returns itself.

Crazy, isn't it?

Well, validation should not return anything except the result of validation, right?

public class ConfirmOrderCommandHandler : ICommandHandler<ConfirmOrderCommand>
{
    private IOrderRepository _orderRepository;
    private ISendEmail _emailSender;
    private IValidator<ConfirmOrderCommand> _confirmOrderCommandValidator;

    public ConfirmOrderCommandHandler(
        IOrderRepository orderRepository,
        ISendEmail emailSender,
        IValidator<ConfirmOrderCommand> _confirmOrderCommandValidator
    )
    {
        _orderRepository = orderRepository;
        _emailSender = emailSender;
        _confirmOrderCommandValidator = confirmOrderCommandValidator;
    }
    
    public async Task Handle(ConfirmOrderCommand command)
    {
        ConfirmOrderCommand validConfirmOrderCommand =
            await command.ValidateUsing(_confirmOrderCommandValidator);

        Order order = await _orderRepository.Get(validConfirmOrderCommand.OrderId);
        order.Confirm();
        await _orderRepository.Save(order);
        await _emailSender.Send(
            new OrderConfirmationEmail(
                validConfirmOrderCommand.OrderId,
                validConfirmOrderCommand.PartnerId,
                validConfirmOrderCommand.PartnerEmail
                )
        );
    }
}

We know, because we are god-like creatures in this little world we're now creating, that CreateUsing throws exception, so we can safely call the outcome of this action as validConfirmOrderCommand.

It's a bit odd that an object of a given type returns exactly the same type.

What qualities does the current setup give?

It might look unreadable, leading to worse maintainability.

Probably, such construct might look unfamiliar to most of our colleagues.

This act of creating a new command out of previous command expresses something.

We can use informal notation of arrows and names of types to show steps in this little process:

ValidateUsing = ConfirmOrderCommand -> IValidator<ConfirmOrderCommand> -> ConfirmOrderCommand

No information about the exception being thrown, but let's forget for a while about it.

Reading it aloud, one might hear: "ValidateUsing is a process that uses ConfirmOrderCommand and uses IValidator of ConfirmOrderCommand and gives ConfirmOrderCommand back."

Can't we say, dear Reader, that the effect of applying ValidateUsing to ConfirmOrderCommand is that we know it is valid?

It is valid.

Before it wasn't, but now it is.

Magic?

Or maybe, there's unspoken, unrevealed concept that is missing?

Let's express it clearly!

public record ValidatedConfirmOrderCommand(
    string OrderId,
    string PartnerId,
    string PartnerEmail
) : ICommand;

public record ValidatedConfirmOrderCommand(
    string OrderId,
    string PartnerId,
    string PartnerEmail
) : ICommand
{
    public static Task<ValidatedConfirmOrderCommand> CreateFrom(
        UnvalidatedConfirmOrderCommand command,
        IValidator<UnvalidatedConfirmOrderCommand> @using
    )
    {
        ValidationResult result = await @using.ValidateAsync(command);
        if(!result.IsValid)
        {
            throw new FailedValidationOf<UnvalidatedConfirmOrderCommand>(result.Errors);
        }

        return new ValidatedConfirmOrderCommand(command.OrderId, command.PartnerId, command.PartnerEmail);
    } 
}

First thing, we changed ValidateUsing to CreateFrom.

Why?

Now we are dealing with two concepts: UnvalidatedConfirmOrderCommand and ValidatedConfirmOrderCommand.

Our process transforms one into another.

We might say that "UnvalidatedConfirmOrderCommand becomes ValidatedConfirmOrderCommand". (you might be interested in Modeling Maturity Levels)

How does it affect the handler?

public class ConfirmOrderCommandHandler : ICommandHandler<UnvalidatedConfirmOrderCommand>
{
    private IOrderRepository _orderRepository;
    private ISendEmail _emailSender;
    private IValidator<UnvalidatedConfirmOrderCommand> _confirmOrderCommandValidator;

    public ConfirmOrderCommandHandler(
        IOrderRepository orderRepository,
        ISendEmail emailSender,
        IValidator<UnvalidatedConfirmOrderCommand> _confirmOrderCommandValidator
    )
    {
        _orderRepository = orderRepository;
        _emailSender = emailSender;
        _confirmOrderCommandValidator = confirmOrderCommandValidator;
    }
    
    public async Task Handle(UnvalidatedConfirmOrderCommand command)
    {
        ValidatedConfirmOrderCommand validConfirmOrderCommand =
            await ValidatedConfirmOrderCommand
                .CreateFrom(command, @using: _confirmOrderCommandValidator);

        Order order = await _orderRepository.Get(validConfirmOrderCommand.OrderId);
        order.Confirm();
        await _orderRepository.Save(order);
        await _emailSender.Send(
            new OrderConfirmationEmail(
                validConfirmOrderCommand.OrderId,
                validConfirmOrderCommand.PartnerId,
                validConfirmOrderCommand.PartnerEmail
                )
        );
    }
}

Well, our handler explicitly collaborates with ValidatedConfirmOrderCommand to create it from incoming unvalidated command and using IValidator.

Also, the handler accepts UnvalidatedConfirmOrderCommand instead of ConfirmOrderCommand - this concept kind of disappeared and turned into two, separate ones.

What qualities did we get by doing so?

One might say we are too expressive and we wrote even more code and achieved little, when it comes to behavior.

If someone looked from outside, well - handler does exactly the same but now works with UnvalidatedConfirmOrderCommand and still has the responsibilty for do something with it.

Wait, wait.

Let's ask our command handler about one thing.

Discussion
Us:Wouldn't it be better if you dealt with an already valid command?
ConfirmOrderCommandHandler:Are you serious? I am waiting entire life for that moment...

When our handler requires an outside collaborator to provide an already valid command, it stops being concerned about validation!

public class ConfirmOrderCommandHandler : ICommandHandler<ValidatedConfirmOrderCommand>
{
    private IOrderRepository _orderRepository;
    private ISendEmail _emailSender;

    public ConfirmOrderCommandHandler(
        IOrderRepository orderRepository,
        ISendEmail emailSender,
    )
    {
        _orderRepository = orderRepository;
        _emailSender = emailSender;
    }
    
    public async Task Handle(ValidatedConfirmOrderCommand validConfirmOrderCommand)
    {
        Order order = await _orderRepository.Get(validConfirmOrderCommand.OrderId);
        order.Confirm();
        await _orderRepository.Save(order);
        await _emailSender.Send(
            new OrderConfirmationEmail(
                validConfirmOrderCommand.OrderId,
                validConfirmOrderCommand.PartnerId,
                validConfirmOrderCommand.PartnerEmail
                )
        );
    }
}

It got leaner - less code in Handle method and we decreased the number of collaborators to two - no need to work with IValidator!

What are the pros and cons of such alternative?

In case of writing tests for handler's behavior, we don't need to have those cases when validation is unsuccessful.

Also, our handler does not change when anything regarding validation changes.

Could we say it got decoupled from validation process?

But is it all butter and flowers?

Nope, unfortunately by decoupling the handler from validation, we decreased understandability.

We might have raised readability because its cleaner (less too read) and very explicit that validation already took place.

It might not be that familiar (depending on the culture, tribe and so on) but hey, we are playing.

About validation, where should we move it?

A new place

If we think in terms of layers, the next "upper" layer is controller - another type of handler - http request handlers category.

It is an option, probably a good one as we have entire structure prepared and it won't be a problem to inject IValidator<UnvalidatedConfirmOrderCommand> and move the validation responsiblity onto the http request handler.

app.MapPost("/orders/{orderId}/confirm/by-partner/{partnerId}",
    async (
        [FromServices] IValidator<UnvalidatedConfirmOrderCommand> validator,
        [FromServices] IDispatchCommands dispatcher,
        string orderId,
        string partnerId,
        [FromQuery] string partnerEmail
    ) =>
    {
        await dispatcher.Send(
            await ValidatedConfirmOrderCommand.CreateFrom(
                new UnvalidatedConfirmOrderCommand(orderId, partnerId, partnerEmail),
                @using: validator
            ));
    }
)

Does not look fancy.

We shuffled it a bit but now everything works!

The command handler is free from validation concern, we express the language and concepts in the more precise way.

But what about qualities?

What problems do you foresee, dear Reader?

HTTP request handler, knows how to perform validation.

It stopped being a mere router but a collaborator that takes part in validation and dispatching a command somewhere else.

Can we do something else?

Going one level down, again

Should we try with yet another handler that will work with UnvalidatedConfirmOrderCommand?

Its main responsibility will be, no shock please, validation.

Yes.

I know that no one writes production code like that and my overengineering skills are boiling your brain, dear Reader, but hey, it's my blog and I can do what I want to.

public class ValidatingConfirmOrderCommandHandler : ICommandHandler<UnvalidatedConfirmOrderCommand>
{

    private ICommandHandler<ValidatedConfirmOrderCommand> _commandHandler;
    private IValidator<UnvalidatedConfirmOrderCommand> _validator;

    public ValidatingConfirmOrderCommandHandler(
        ICommandHandler<ValidatedConfirmOrderCommand> commandHandler,
        IValidator<UnvalidatedConfirmOrderCommand> validator
    )
    {
        _commandHandler = commandHandler;
        _validator = validator;
    }
    
    public async Task Handle(UnvalidatedConfirmOrderCommand unvalidatedConfirmOrderCommand) =>
        await _commandHandler.Handle(
            await ValidatedConfirmOrderCommand.CreateFrom(
                unvalidatedConfirmOrderCommand
                @using: validator
            ));
}

And our HTTP request handler again becomes a mere router:

app.MapPost("/orders/{orderId}/confirm/by-partner/{partnerId}",
    async (
        [FromServices] IDispatchCommands dispatcher,
        string orderId,
        string partnerId,
        [FromQuery] string partnerEmail
    ) =>
    {
        await dispatcher.Send(
            new UnvalidatedConfirmOrderCommand(orderId, partnerId, partnerEmail)
        );
    }
)

We maxed this overengineering.

What are attributes that are yielded through such structures?

Better testability?

Better readability, worse understandability?

Clearer reasoning?

Too much small classes?

Too many "layers"?

Too much indirection?

One might think: "haha, he just discovered Decorator pattern!".

The more we were decomposing by responsibility, the more "design pattern-ish" it started looking like.

Each "thing" does pretty much one job.

Of course, it does not come for free.

What we are paying with?

Designing means (consciously) working with options

Each step that we visited, either "gave" us something or "took" something from us.

This was a toy example but I believe we were able to go through various ways of organizing code. (ways of organizing? You might be interested in The ambiguity of software architecture)

WAIT!

Can you notice what just slipped through?

"Ways of organizing code", was it a code that we organized in different topologies?

I think it was more than just "code".

"Code" is just a model, a way of representing something.

Through our journey we were decomposing, re-organizing stuff and assigning responsibilities in various configurations.

The ambiguity of composition does not help too - there is no single way of composing "things" together.

We played with a variety of structures leaving the behavior unchanged.

At some point, each structure was working as a boundary for a particular responsibility: validation of an incoming order confirmation or processing a valid order confirmation.

Now, we even can give proper names for each step of the given flow - previously everything was molded together in an unnamed pulp (and this "pulp" was located in one gigantic-to-be command handler).

Don't get me wrong, dear Reader - nothing is wrong with keeping everything together, in one gigantic pile - such organization of responsibilities also yields some qualities!

Designing or just coding?

In some languages, decomposition (and later composition) comes with a (little) higher price than in others.

The amount of so-called "boilerplate", to form a proper structure, might require more effort - which does not encourage a fellow designer or a problem solver to make it happen.

Often, such process of forming a structure might be even called "an overengineering".

What are criteria for such state?

Those are highly contextual and should be evaluated independently.

But there's no time for evaluation - you just want to "ship that sh*t."

Shipped sh*t has a very interesting quality - it attracts more sh*t.

If I were a functional programmer (what does it mean to you, dear Reader?) I could say that "sh*t structure" forms a monoid - you can append sh*t to sh*t, getting even bigger sh*t.

Code needs structure to support certain qualities - what qualities do we want to get from the "code" (think structure)?

Completing the ticket? Providing the feature to quick feedback and painless change later?

No options = no designing?

It might sound like a bold expression: no options = no designing?

Yet again, "always" or "never" smoothly enters the game and destroys everything.

I don't mean we need to sit and analyze all possibilities for each, even tiny feature.

My engineering mind wants options - and to get options I need to know ways of organizing stuff around.

Aggresively decomposing collaborators around (yes, each doing precisely one job - like an if expression or LINQ operations piped together) yields the understanding what responsibilities I might need to establish to get the job done.

We quickly might get the feeling where things should be coupled and to it intentionally - before decomposition, we might not even notice that (why? Please check Modularity Uncertainty Heuristic).

Still, we can't avoid the law of eventual composition and all separated parts will need to integrated to deliver the whole experience.

Next time dear Reader, while "coding", Slow down, and consider at least three options so that you start designing and stop coding, by asking yourself:

Question 🤔

What structures and collaborator communication patterns would support desired qualities?