what's the point of query/command objects in CQRS?

What is the point of having an additional transfer object?

Why is

cookingRecipeCommandHandler.Handle(new CreateCookingRecipeCommand(...)); 

better than

cookingRecipeHandler.CreateCookingRecipe(...);

I'm having a hard time to understand what the advantages of these additional objects are supposed to be.

2 answers

  • answered 2020-11-23 06:22 VoiceOfUnreason

    What is the point of having an additional transfer object?

    Re-usable composition.

    cookingRecipeCommandHandler.Handle(new CreateCookingRecipeCommand(...)); 
    

    Typically, the line of code above would come about because we had code somewhere like

    class CookingRecipeCommandHandler : Handles<CreateCookingRecipeCommand>
    

    where Handles would be something like

    interface Handles<T> : where T:Command
    

    and then things other than your command handler can implement the same interface and be chained together:

    class Logging<T> : Handles<T> : where T:Command {
        Logging(Handles<T> next) { ... }
    
        void Handle(T command) {
            log(command)
            next.Handle(command)
        }
    

    In other words, by designing all of your specialized command handlers so that they have superficially similar signature, they can all be easily composed with one or more general purpose handlers that share that common signature.

    See: 8 Lines of Code by Greg Young.

  • answered 2020-11-23 13:55 jgauffin

    Without Command/Query objects, applications are typically designed around central classes for each type of entity. We have service classes like CookingRecipeService (also called managers or handlers).

    They take care of everything related to an entity type.

    That's fine to start with. But as applications age, they get more and more functionality which means that the service classes grow. Their methods also tends to grow as using private methods to solve small sub tasks makes them even harder to read.

    With command objects you get a much cleaner design since you typically have a specific class to solve a specific command. That also invites into dividing the task into smaller sub tasks (i.e. private methods) which makes the code much easier to read (and therefore to maintain).

    In CQRS we talk about two distinct parts in an application. The write side which updates the state of our objects (through commands) and the read side which supplies information to the UI.

    That distinction is also important as the UI seldom only requires information from a single entity. It typically wants compound information (for instance the user name of the recipe maker, the recipe, rating, comments etc). A service/manager class isn't a great fit for that while query handlers can be specialized to aggregate the information.