Passing Commands directly to Aggregate-Roots

802 views
Skip to first unread message

David Rettenbacher

unread,
Feb 28, 2017, 4:42:46 PM2/28/17
to DDD/CQRS
I'm looking for advice on aggregate-roots and commands.

Given the following command:
public class SetNewPriceCommand
{
   
public Guid Id { get; set; }
   
public decimal PriceValue { get; set; }
   
public string PriceIsoCode { get; set; }
}

I learned that domain-objects like aggregate-roots should not directly hande commands. If the app receives a command it has to transform the contents of the command like ids, price-value, price-currency on the application-layer (web-api) to domain-objects like ArticleId and Money. These values should then be passed to the loaded aggregate-root's method as parameters.

public class Article
{
   
...
   
public void SetNewPrice(Price price)
   
{
       
// validate if price can be set
       

       
// apply change
   
}
   
...
}

At work the current way we program aggregate-roots differently. Aggregate-root methods get their commands directly as a parameter.
To me this seems plain wrong, because commands are just simple DTOs, containing only basic value types and no domain-objects like value-object or the like. I think converting transforming a command (or better said its contents) to domain-objects should be done on application-layer, not inside the domain.

public class Article
{
   
...
   
public void SetNewPrice(SetNewPriceCommand command)
   
{
       
// validate command
       
// validate if price can be set
       

       
// map command
   
}
   
...
}


As a team we are trying to update our current coding guidlines so this would be a chance to change this, but there is sceptism if not directly passing the command would result in more code et cetera.
I'm trying to find some arguments - pros and cons, even for both ways.

What I want to ask you
Is it considered "acceptable" to pass commands directly to aggregate-root methods?
- If yes, why? (and could you provide some (renowned?) sources)
- If no, why? (and could you provide some (renowned?) sources too)

Thank you in advance!

Greg Young

unread,
Feb 28, 2017, 4:45:09 PM2/28/17
to ddd...@googlegroups.com
It being most people are doing this work in Application Services or
Command Handlers, are you not just violently agreeing with them?
> --
> You received this message because you are subscribed to the Google Groups
> "DDD/CQRS" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to dddcqrs+u...@googlegroups.com.
> Visit this group at https://groups.google.com/group/dddcqrs.
> For more options, visit https://groups.google.com/d/optout.



--
Studying for the Turing test

David Rettenbacher

unread,
Feb 28, 2017, 5:03:57 PM2/28/17
to DDD/CQRS
Application services and command handlers are part of the anti-corruption-layer, right? So no problem there for me.


"are you not just violently agreeing with them?"
I'm not quite sure if I understand that correctly (no native speaker), but transforming command to domain-objects in the application-layer is something different than passing commands directly to the aggregate-root itself, so I "violently" disagree with them that passing a command-object directly to AR is an "acceptable code smell".
(Please rephrase your question if I got it wrong :))

Greg Young

unread,
Feb 28, 2017, 5:09:09 PM2/28/17
to ddd...@googlegroups.com
passing commands directly to the aggregate introduces another set of
issues depending on what language/tooling you are using. As example
how are dependencies managed? In Java/C# this becomes an issue, in
erlang not so much.

On Tue, Feb 28, 2017 at 10:03 PM, David Rettenbacher

David Rettenbacher

unread,
Feb 28, 2017, 5:16:33 PM2/28/17
to DDD/CQRS
We're using C# and dependencies are basically managed by a hidden service-locator antipattern (calling DependenyResolver.Current.GetService(...) in an Initialize()-method).

@Dependencies:
I personally would perfer removing the service-locator and pass serivces to methods as required by the operation.

Alexandre Potvin Latreille

unread,
Feb 28, 2017, 5:35:11 PM2/28/17
to DDD/CQRS
Here's what I had answered on StackOverflow about passing commands directly to ARs.

David Rettenbacher

unread,
Feb 28, 2017, 5:45:23 PM2/28/17
to DDD/CQRS
Thanks Alexandre, I share the views in your answer.

Thomas Schanko

unread,
Mar 7, 2017, 6:05:21 PM3/7/17
to DDD/CQRS
Command handlers and application services only have little in common with anti corruption layers. You might find it helpful to look for 'Ports and Adapters' or 'Hexagonal Architecture' and compare that to the intention behind an ACL. Somehow it's all about boundaries, but context matters;-)
Doing things like all the other people do 'em is probably a good idea to start with. Pick some small part of your domain, implement it following all the rules, lay back and look at the code you came up with. Does it feel right? Look at the relation between plumbing code  and the meaningful stuff. Carefully observe what you and your team experienced, distinguish between stuff that works and delivers value and stuff that sucks and keeps you from getting somewhere before trying to come up with coding guidelines. From my experience, improving code quality  is rather about setting up team culture then about written coding guidelines. 

But what do I know?

Thomas

nzp...@gmail.com

unread,
Mar 10, 2017, 4:31:46 AM3/10/17
to DDD/CQRS
Like most things it depends :-)

If the commands contain or have dependencies on infrastructure, e.g. routing information, then I'd say definitely no as they're bringing pollution into your domain.

If the commands are just simple DTO's that encapsulate the parameters of the method, and nothing more, then they are perfectly acceptable or even positive pattern.

The current implementation I'm working with embraces the command event pattern with aggregates using a convention of command parameters and event return types. e.g.
       
        public AdjustedIn When(AdjustIn command)
        {
            //snip
        }

which has lead to some very readable code, especially in unit tests.
Reply all
Reply to author
Forward
0 new messages