I'm a seasoned programmer but a novice with DDD. I have a project that I'm trying to implement with DDD and my understanding is that there is some ambiguity with regard to the use of domain models and application services. In the purest of DDD implementations, my understanding is that domain models are supposed to contain all business logic. However, this doesn't seem like a realistic goal to me. In simple validation scenarios and such, this is all well and good, but the goal of encapsulating business logic in domain models gets a little slippery when we think about logic requiring additional services such as database lookups/persistence and other external dependencies.
For example, I have a domain model "Order" that has a method "IsValid". To determine whether an order is valid, a database read must be performed. In order to keep the model clean, I don't want to inject any database work into it. OK fine – I create an OrderService to do the database hit and pass in a result to "IsValid" so that the dependency on the database resides with the service. However, what happens if there are multiple database hits that depend on business logic? Is it appropriate to add that logic to the application service? If so, it seems that business logic is leaking into the application services layer which is a violation of DDD. However, if we move the logic inside the domain model and do the database hit there, we're mixing concerns and also violating DDD.
Can anyone experienced with DDD provide any insight into how to deal with this sort of an issue? DDD seems like it provides a lot of nice value, but I'm worried about these kinds of issues leading to design "rot".
Best Answer
This is a very broad question but I will try to give you an answer.
There is no ambiguity if you design well your bounded contexts, the domain models and the relationships between them.
In
DDD
, all the operations go through theAggregate root
(AR
). Theapplication services
load theARs
from the persistence, send commands to them, then persist thoseARs
back.ARs
don't need to hit the database at all. In fact a good designedAR
does not even know that databases exists at all. All they touch/see/smell is their internal state and the immutable arguments that they receive in their command methods. If anAR
needs something from the database then theApplication service
pass that thing as argument.ARs
should be pure, side effects free objects/functions. One reason is that commands applied on them must be retry-able, in case of concurrent modifications.As an example:
ARs
don't send emails, they return avalue object
that holds the email data (from, to, subject and body) and the Application service takes that value object and passes it to a infrastructure service that does the actual sending of the email.You don't need an
isValid
method, as anOrder
cannot get into an invalid state anyway because any modifications are done through it's methods. If you are referring to the existence of anOrder
then this kind of validation is done by theApplication service
: if it does not find theOrder
in the persistence then it does not exist, as simple as that. Maybe you are referring to theShoppingCart
as being valid, not theOrder
. What about then? Well, you could try to create anOrder
from aShoppingCart
and if you succeed then aCart
is ready to be ordered. As theOrder
is side effect free, no order will actually be created. Just an example of how you might think inDDD
.If you follow the
DDD
approach well, your design will never rot. Never.As a foot note: In the beginning I was a little miss-leaded by the Layered architecture. Don't make the same mistake. Take a look at some newer architectures like
CQRS
that fits very well withDDD
.