Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing CQRS commands directly to Domain objects

~TLDR: I'm implementing a CQRS + DDD solution for one of my larger projects, and, I'm wondering if there is any real reason that my command handlers can't directly dispatch the command objects to my aggregates, in a small handful of cases, where the command object is data rich? I can't find any specific reason why this would be any kind of an anti-pattern, and I can't find any opinions that go into great detail about this type of design.

Background: I have implemented CQRS systems before, and I have implemented DDD applications, but never CQRS + DDD in a proper Eric Evans style domain driven application. So I ask because I don't want to abuse my Aggregates, and hurt my application in the long term.

An example of my command object having quite a bit of data would be a registration command that takes in 8+ fields (firstname, lastname, preferred name, dob, title, username, password, department etc). It feels very awkward creating a method on my Aggregate that has 8 params, and the alternative solution of using some sort of dto, and having my handler map the command to the dto - either automagically using automapper, or inline - seems like an unnecessary and non value adding abstraction.

I can also see future use cases where commands might be data rich (it wouldn't be a large percentage of commands, but there would still be a few), so I'd like to get this seemingly trivial aspect correct from the start.

like image 285
AaronHS Avatar asked May 25 '15 08:05

AaronHS


People also ask

Are CQRS commands part of the domain model?

CQRS commands and the onion architectureCommands belong to the core domain (just like domain events). They play an important role in the CQRS architecture - they explicitly represent what the clients can do with the application.

Should commands be immutable?

Commands are immutable because their expected usage is to be sent directly to the domain model side for processing. They do not need to change during their projected lifetime in traveling from client to server. Events are immutable because they represent domain actions that took place in the past.

Is CQRS asynchronous?

It's always in-sync. CQRS is rarely synchronous because we want to work/scale with different resources, type of databases, use a messaging infrastructure: we can rarely make a distributed transaction cross-resources (we could talk about XA or Sagas but… not now!).

Are queries part of the domain?

Domain events, commands, handlers are technical details. However all domain use cases are usually implemented as a command handler, therefore command handlers should be part of the domain as well as the query handlers implementing queries used by the domain. Queries used by the UI can be part of the UI and so on.


2 Answers

Command objects are usually expressed in primitive types while aggregate method signatures will be expressed in domain concepts.

The fact that you didn't immediately recognized that probably means that you missed a lot of opportunities to make implicit concepts explicit in your domain.

"a registration command that takes in 8+ fields (firstname, lastname, preferred name, dob, title, username, password, department etc)"

What should strike you is that firstname and lastname could definitely form a meaningful whole, such as new FullName(firstname, lastname) and I'm sure there's a lot of other cases where Value Objects (VO) could or should be used in your domain... Username, Password, etc. ? Using VOs to model things that changes together will better describe your model as well as reduce the number of arguments you have to pass around.

Therefore, that makes command objects poor candidates as aggregate method arguments. If you go down that road, you will definitely miss modeling opportunities.

like image 183
plalx Avatar answered Sep 20 '22 17:09

plalx


Agree with @plalx.

Take commands as aggregate method arguments may lead to having too many mapping codes inside aggregates: Mapping primitive types to domain objects, which is better to be placed out of the domain objects.

But for simpler projects, I think it is good starter.

In registration case, the bounded context is usually a supporting domain and the complexity usually comes from external integration(email notification, register with social accounts, etc). In this case, I think bounded context integration is more important than the models inside. So take commands as aggregate method arguments may be a quick start to get things done and saves your time to focus on your core domain.

like image 21
Yugang Zhou Avatar answered Sep 20 '22 17:09

Yugang Zhou