Reputation: 1
I am new to Domain Driven Design, and I am struggling with where to invoke calls to persistence, locking and/or internal and external services. In the aggregate root? In a surrounding application or domain service? Or somewhere else? And why?
As an example, let's consider a shopping cart. It should support:
In our company, we are all very new to DDD, and we have a lot of different opinions. We have experience with (virtual) actor frameworks like Darlean where actors are similar to aggregate roots and are fully self-contained, so they provide their own locking, invoke persistence when they need, invoke other services, et cetera. On the other end, we use an approach where our aggregate roots are as minimal as possible, contain very little business logic, and do not invoke external or domain services at all. All of that is performed in an application service around the aggregate roots.
What is your opinion/expertise/advise? Where to put external service invocations / domain service invocations / persistence invocations / locking invocation? In the aggregate root? In a surrounding application or domain service? Or somewhere else? And why?
Upvotes: 0
Views: 78
Reputation: 57249
Peristing state.
Evans discusses this in the original book: transaction control belongs outside of the domain model (see the discussion of REPOSITORY in Chapter 6).
Adding a shipping address Adding products to the cart
In the ideal case, your domain model's responsibilities are limited to the manipulation of local representations of domain information. Retrieving and distributing domain information is generally regarded as an application concern, rather than a domain concern.
One benefit to this approach: your domain code doesn't get cluttered with a bunch of logic designed to compensate for the fact that you are running in a distributed environment. In your specific instance, we're trying to isolate your shopping card logic from the realities that HTTP calls can fail.
In the general case, it can be unsatisfactory to have the application code fetch all of the data (because fetching information is expensive and not always necessary, or because we don't know what information to fetch until the domain logic runs). The common solution here is to pass the capability to fetch the information as an argument, usually in the form of a "domain service". This doesn't eliminate the need to compensations, but may allow you to use implicit handling of those conditions (aka "exceptions"), avoiding pollution of your domain code.
An alternative approach calls for developing a sort of protocol between your application code and your domain code, so that the domain code can ask for what it needs, and the application code can fulfill that need or handle the failures.
Synchronization/locking
As best I can tell, this is still something you want to manage outside of the domain model (why should our shopping code need to know or care how many copies of it are running, or what other processes manipulate the same information?)
There's not, that I can think of, a lot of literature on this point. Probably the best place to start is Pat Helland 2005. As best I can tell, the real consensus is "try very hard not to have this problem".
Upvotes: 0
Reputation: 1
Agregate root Technical details such as external service calls or persistent calls should not be available here. The agregate root should ensure the focus of business logic and manage the core business processes.
My suggestion is to organize the technical details in a surrounding application or domain service, while keeping the core business logic at the total root. This ensures the code remains clean and there is a clear separation of responsibilities
Upvotes: 0