Reputation: 171
I have this classic DDD problem; I have a Domain Service "DetectPriority" that do some stuff.
PM ask me to create 2 different services; one INTERNAL ( with is FULL of business rules and involve many other Domain Models ) and another one ETERNAL ( a simple API call ).
There is a interface "DetectPriorityInterface" within the Domain. Both Implementations MUST be active in the same time; a kind of "mixed" has to select one instead of the other in real time.
The problem is: Where do these implementations ( two implementations ) should live: in Domain Layer or Infrastructure Layer??
Internal Implementation is full of business rules and should reside in Domain Layer. External Implementation is a simple CALL and should lives in Infrastructure.
Should we put both in Infrastructure layer?
Thanks
EDIT
Actually we have one interface "DetectPriority" and three implementations, ALL in our Domain layer ( temporary "wrong" solution ) :
Clients use the Interface so, for Application Layer, all these stuff are trasparent; in the next, we will remove the Internal ( or External ) and Mixer and use only One Implementation. ( The idea behind all these is to understand who performs better, it is an A/BN test )
Our internal debate is: Cause InternalDetector has some domain rules that belongs ONLY to that Detector, it should live in Infrastructure layer, cause it is not an General Domain Rules. Some of us disagree with this, cause in InternalDetector we only have business rules and we don't see that in Infra Layer.
Problably the correct way should be add Internal in Domain, and External in Infra .. but it seems to be a bit confused ..
Putting all together in Infra layer would be more readable for devs...
We didn't find some stuff in books cause usually you have a single implementation of a domain service ....
Upvotes: 2
Views: 1840
Reputation: 608
The short answer is that you should implement an internal service in the domain layer and an external one in the infrastructure layer, exactly as you said in your question. This way, everything will be in its place.
An additional important thing to consider is that the code that decides which service to call should be in the domain layer too. As I can imagine from your question, you decide which detector to use using some business rule. That fact that one detector is implemented in your application, and another one is not is just an implementation detail of your system. In fact, you just decide to use one set of business rules or another one according to some condition. It is a business decision.
DDD is pretty often about difficult compromises. But when you are looking for a good compromise, I would advise never move the domain logic outside of the domain layer, and never add references from the domain layer to other ones.
This is essential.
And here is an example of how you can solve this task without violation of these rules:
// Names in this code should be changed to something with business
// meaning. For example `externalDetector` can be `governmentDetector`
// and `internalDetector` can be `corporateDetector`.
// Declare a service interface in the domain layer
public interface DetectPriority {}
// Inject both detectors in the domain service.
// Your dependency injection code should inject here
// an internal implementation and an external one,
// implemented in the infrastructure layer.
// So your DI code knows about different implementations
// but the domain service doesn't.
// For the domain service it's just two implementations
// of domain interface IDetector
IDetector _externalDetector;
IDetector _internalDetector;
// Implement the method of the domain service like this:
public Priority Detect()
{
if (weShouldUseExternalSetOfRules)
{
return _externalDetector.Detect(); // this one is implemented in your infrastructure layer
}
else
{
return _internalDetector.Detect(); // this one is implemented in your domain
}
}
In this solution you can see that:
IDetector
interface, but this interface is declared in the domain layer.externalDetector
implementation.To be sure that it is a good way, you can take a look at this repository with a sample DDD application from famous Eric Evans' book. You can find there a service interface declared in the domain layer and the service itself implemented in the infrastructure layer. Unfortunately, there aren't examples of using this service interface inside of the domain layer in this application. But it's declared inside of the domain layer to make such a usage possible.
And you can find the same approach with a good explanation in this great article.
EDIT
According to new information in the question, if it is about A/B testing, then choosing a detector is the application-level decision. All other things remain the same. So:
MixerDetector
should be in the application layerDetectPriority
interface - in the domain layerInternalDetector
in the domain layerExternalDetector
in the infrastructure layerAnd you don't need "business" names for your detectors then, because they are literally InternalDetector
and ExternalDetector
.
Upvotes: 4
Reputation: 3911
I use to keep domain service implementations which are free of infrastructure dependencies in the domain layer. Implementations of a domain service interface which require infrastructure dependencies should reside in the infrastructure layer.
What you need to consider as well in your case is that the code which instantiates the concrete implementation of your DetectPriorityInterface at runtime has to reside in the infrastructure layer as well as it also has a direct dependency to the external domain service.
I suggest you have some factory for that job which decides on creating one or the other domain service based on some kind of parameter. But you can still use a factory interface which you can put in your domain layer. Let's call it PriorityDetectorFactoryInterface or similar. And only the concrete implementation of that factory, let's call it PriorityDetectorFactory would reside in the infrastructure layer.
If you have some application service which is responsible for handling the use case where the priority detection comes into play you would pass the PriorityDetectorFactoryInterface into this application service. At runtime the concrete implementation of the factory interface (i.e. PriorityDetectorFactory) will be injected into the application service. With that you can also keep the application layer where you usually only define the workflows for orchestrating your use cases free of infrastructure dependencies.
With that you would have:
Note: this is all based on the assumption that your internal domain service implementation is really free of dependencies other than stuff from the domain layer itself.
Upvotes: 0
Reputation: 57194
Should we put both in Infrastructure layer?
Not usually, no. Among other things, that's going to make a mess of your dependency graphs. We don't want domain code to have a dependency on infrastructure code (one of the motivations for having a domain model is so that you can implement the logic of the domain without being tightly coupled to the context that runs the domain model -- introducing infrastructure dependencies is contrary to that goal).
That doesn't necessarily mean that the infrastructure code is "far away" - see package by feature vs package by layer. They are different responsibilities (in the single responsibility principle sense), so there will usually be some separation between the two.
One aspect in which the two are very different: failure modes -- robust code that communicates across the network needs to respect the fact that the network is unreliable, but that's not a domain concern, so we don't usually want to pollute our domain code with network contingency logic.
But if you like, ignore all that -- the real heuristic is simple: we want the arrangement of code that is easiest to maintain over its entire lifetime. If in your context that means putting domain code into the infrastructure layer, then that's what you should do.
The guidelines of DDD and other styles are primarily there to help you avoid the trap of increasing the lifetime maintenance cost by deciding to do what is easy "right now".
Upvotes: 0