This is a very broad question but I will try to give you an answer.
...there is some ambiguity with regard to the use of domain models and application services
There is no ambiguity if you design well your bounded contexts, the domain models and the relationships between them.
However, what happens if there are multiple database hits that depend on business logic?
In DDD
, all the operations go through the Aggregate root
(AR
). The application services
load the ARs
from the persistence, send commands to them, then persist those ARs
back. ARs
don't need to hit the database at all. In fact a good designed AR
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 an AR
needs something from the database then the Application 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 a value 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.
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.
You don't need an isValid
method, as an Order
cannot get into an invalid state anyway because any modifications are done through it's methods. If you are referring to the existence of an Order
then this kind of validation is done by the Application service
: if it does not find the Order
in the persistence then it does not exist, as simple as that. Maybe you are referring to the ShoppingCart
as being valid, not the Order
. What about then? Well, you could try to create an Order
from a ShoppingCart
and if you succeed then a Cart
is ready to be ordered. As the Order
is side effect free, no order will actually be created. Just an example of how you might think in DDD
.
DDD seems like it provides a lot of nice value, but I'm worried about these kinds of issues leading to design "rot".
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 with DDD
.
You are asking the fundamental question of architecture: "I have a lot of logic...how do I structure it?" It is hard to answer such a general question in brief since numerous books have written about various aspects of this problem.
But in short, you probably suffer from "God objects", and names like "XxxHelper" indicates classes does not have a clearly defined purpose. It is much easier to think of a meaningful name if the class have a delimited and well-defined purpose.
You have to start by separating the code into modules and classes with clearly defined purposes. An approach would be to draw sketches or mind-maps of all the tasks and operations in the business logic and try to group them into layers, subsystems, core, services etc. It seems you already have this principle down for the overall architecture, so you need to apply the same design process to the business logic.
The fundamental design principles: Layering, slicing, separation of concerns, single-responsibility, high-cohesion-low-coupling and so on should be applied at all levels of the architecture, not just the top level.
If you want a more formalized approach, you can look into Domain Driven Design, which describes a number of approaches and patterns for structuring business logic (or domain logic, as it is also called, which is basically the same thing).
Best Answer
You might want to take a look at various open source Business Process Management suites. BPM is this exact idea, identifying the process a business takes to effect some activity, and mapping it into software in a way that manages the flow of the process. It seems like it would lend itself well to your issue. I work professionally with IBM BPM, but that is a proprietary and non-free solution, and is probably overkill for what you might be looking to achieve, which is why I provided a list of open source ones. I suggest looking through how those platforms/framworks operate on a conceptual level, even if they aren't the direct implementation of a solution for you, understanding how they handle the concept can be useful to you in 'rolling your own' solution.