user3789553
user3789553

Reputation: 151

Domain-drive-design vs. Command pattern — mutually exclusive?

tl;dr:
I like the Command-pattern's small, focussed classes such as SetProjectAsActiveCommand but I also DDD's approach of making models responsible for their own core business functions, such as calling Project::setAsActive(). Can the 2 ideas work together, or are they mutually exclusive architectures?
/ tl;dr

I've been working on a project for the last few months in which we've been using the Command pattern, which has classes like ProposeNewProjectCommand, SetProjectAsActiveCommand, and AddCommentToProjectCommand, all of which are handled by a command bus. These classes tend to be fairly small and focussed, doing only one thing.

Recently I've been reading about Domain-Driven-Design (DDD) and I gather that that approach relies more heavily on the Models doing work themselves, so the commands above might be replaced with Organisation::proposeNewProject(), Project::setAsActive(), and Project::addComment().

Having these methods on Models means they can act as 'aggregate roots' (eg. in the examples above, Project is responsible for creating its own comments).

Whilst I really like the idea of making my Entities have more responsibility for their core business functions I'm also concerned that my Models could get really, really big with lots of methods on them relating to the various things the application can do.

Is there a way to have the small, focussed classes of the Command pattern whilst still making Models first-class citizens responsible for their key business functionality like in DDD? Or a way to tackle the potential large-model problem?

Or, alternatively, should I just pick one pattern and use it exclusively?

Thanks in advance for any insight you can give,
Harrison

PS. I've never actually worked on an application which uses DDD, so I apologise if this is an incredibly naïve question.

Upvotes: 3

Views: 1142

Answers (2)

Mathias V
Mathias V

Reputation: 759

What makes it DDD, is that you start thinking about language more, that you're trying to get to a model in the code that "syncs" with your understanding of the domain. The actual implementation patterns are, well, implementation details. For example, we can try if there's even better ways of expressing the commands, eg SetProjectAsActiveCommand -> ActivateProject; AddCommentToProjectCommand -> CommentOnProject. We get rid of the developer language (set, add, command) in favour of the domain language.

Commands, in this approach, don't do anything (they're not like GoF commands). They are simply messages, representing a user's intention. Aggregates (and entities, value objects, ...) represent our structural domain model. They know how to do things, make decisions, protect business rules, ... In between those messages and the decision making, there needs to be something that receives the message and translates it to the domain model. Those are the command handlers: they're fairly dumb, they just make sure the message reaches the right aggregate. So very often, a command handler does little more than fetching an aggregate from a repository and tell it to do something, without understanding internals. This neatly decouples commands from the domain model.

You can probably tell I'm a big fan of this style of code organisation. I think it's very useful for DDD. You can apply DDD in many other ways. In the end, the goal is not to "do DDD", but to build systems that solve problems, are testable and maintainable and evolvable.

Upvotes: 7

Tyler Day
Tyler Day

Reputation: 1718

I would say they can absolutely be used together. In fact, we use this combination with great success on our current projects.

You are correct that the command handlers are fine grained, while the DDD models are more cohesive. However these command handlers are free to perform the command in a variety of ways, be it DDD or transaction script. You can use whatever approach makes sense for the given command/use case. So you are not locked into using DDD for every command (sometimes you just need a simple insert without a model involved).

Your DDD models can be shared throughout your command layer. For example, you may have 5 customer commands that all operate on one DDD Customer model object.

Upvotes: 1

Related Questions