Understanding the Command pattern in a DDD context

2019-04-14 19:11发布

问题:

I was recently reading this article here: https://cuttingedge.it/blogs/steven/pivot/entry.php?id=100. It appears to talk about using commands (http://www.dofactory.com/net/command-design-pattern) instead of application services.

Please see the code below:

public sealed class ShipmentController
{
    private readonly ICommandDispatcher dispatcher;

    public void ShipOrder(ShipOrder cmd) => dispatcher.Dispatch(cmd);
}

sealed class CommandDispatcher : ICommandDispatcher
{
    private readonly Container container;

    public void Dispatch(dynamic cmd) => GetHandler(cmd.GetType()).Handle(cmd);

    private dynamic GetHandler(Type type) =>
        container.GetInstance(typeof(ICommandHandler<>).MakeGenericType(type));
}

which replaces code like this: http://www.zankavtaskin.com/2013/11/applied-domain-driven-design-ddd-part-6.html

I have three questions:

1) Is this saying that you should have one command per command request in the Application Service Layer? Wouldn't this result in class explosion e.g. if you have 100 commands?

2) What do you do with CQRS queries? Do you create regular application services for these?

3) What do you do with scenarios where you extract from the database (say an order); perform a command on the Order e.g. CalculateTax and then persist to the database? I assume the flow would be (is this right):

MVC 
Application Service (to extract order from database)
Command (Application Service calls CalculateTaxCommand)

回答1:

It appears to talk about using commands instead of application services.

No, it does not talk about the command design pattern. There is a very clear, and crucial distinction between the command design pattern, and the CQRS-like patterns that are described on my blog and elsewhere.

The 'command' in the command design pattern combines data and behavior within the same class. With CQRS, the command is simply a message with no behavior. Behavior is moved to a 'handler' class. This separation is the driver that enables the maintainability and flexibility of this design.

1) Is this saying that you should have one command per command request in the Application Service Layer? Wouldn't this result in class explosion e.g. if you have 100 commands?

This is a very common misconception with developers. They think there is a direct relationship between the number of types in the system and the maintainability of that system, where an increase in classes means a decrease of maintainability.

The SOLID design patterns however favor small and focussed classes over big classes, because making classes smaller, can actually increase the maintainability of a system tremendously.

This is exactly what's going on here. This design should be viewed from the point of SOLID. My experience that, within the systems I refactored to that model, we saw a massive increase in maintainability in an order of magnitude, even though the amount of classes would as well increase with an order of magnitude.

Do not worry about the number of classes in your system. Just worry about maintainability.

This doesn't mean that project structure is irrelevant. It isn't. So find a good project structure for your commands, their handlers and their decorators.

2) What do you do with CQRS queries? Do you create regular application services for these?

You do exactly the same to queries as you do to commands. Each query should have its own query message and a handler, and optionally a result message class. This blog post describes how to design you queries.

3) What do you do with scenarios where you extract from the database (say an order); perform a command on the Order e.g. CalculateTax and then persist to the database? I assume the flow would be (is this right):

This is an atomic operation and should all be part of the command. When the command is executed, the order is loaded from the database, based on its ID captured in the command. The tax is calculated and the order is persisted as part of that (business) transaction.