Search code examples
c#domain-driven-designcqrsdtomediatr

CQRS with MediatR and re-usability of commands


Does it make sense to create commands that just hold objects? For example:

public class CreateCommand : IRequest
{
   SomeDTO SomeDTO { get; set; }
}

public class UpdateCommand : IRequest
{
   SomeDTO SomeDTO { get; set; }
}

Or perhaps something like this (deriving):

public class UpdateCommand : SomeDTO, IRequest
{
}

Or commands/requests should be treated as DTOs themselves? I'm confused because I saw many ways of doing things. Also copying all properties to command/request classes doesn't sound like a nice thing to do.

How do you do this in your projects?

Do you map your commands directly to your domain models or you use commands just to pass DTOs?

In case of using MVC framework what should be the input of my controller actions? Should it be a command, or should I create command inside my action implementation and send it? (I guess that will depend on how I model my commands)


Solution

  • Commands and domain objects, at least in my world, have different design constraints. In particular, commands are part of the API surface - they are part of the contract with other services - and therefore need to have compatible definitions over long periods of time. Domain objects, on the other hand, are local to our current way of doing things - they are part of our organization of data within the black box. So we can change those at any cadence we like.

    Commands that cross process boundaries are messages, which is to say byte[]s. That's the bit that needs to be stable, both in form and semantics.

    byte[] is domain agnostic, and it's fairly common to pass through several other domain agnostic intermediate stages in "parsing" the message

    byte[] -> utf8
    utf8 -> DOM
    DOM -> Dictionary
    ...
    

    but we're generally driving toward a domain specific expression of the contract.

    See, for instance Mark Seemann

    At the boundaries, applications are not object-oriented. A DTO is a representation of such a piece of data mapped into an object-oriented language.

    Having coerced the byte[] into a form that is convenient for querying, then we can start thinking about whether or not we want to use that data to start initializing "objects".

    The other question that you may be asking - is there value in a having the message data within a generic metadata "envelope". That kind of pattern occurs all the time - the most familiar example being that an HTTP POST is a bunch of generic headers attached to a message-body.

    The data and the metadata are certainly separate concerns; it definitely makes sense to keep them distinct in your solution.

    I think compositing the data structures, rather than inheriting them, is going to be the more maintainable option.

    public class Envelope<Message> ....
    

    might be a reasonable starting point.