GFCoder977
GFCoder977

Reputation: 171

DDD - Domain Service Implementation: Domain or Infrastructure

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 ) :

  1. InternalDetector ( with Business Rules )
  2. ExternalDetector ( With external API call )
  3. MixerDetector ( get both Implementations and handle the choise )

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

Answers (3)

Andrei Prigorshnev
Andrei Prigorshnev

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:

  1. All domain logic (an implementation of internal detector and decision which set of rules to use) is placed in the domain layer.
  2. We don't have references to the infrastructure layer from our domain. The domain service have the reference only to IDetector interface, but this interface is declared in the domain layer.
  3. There is not infrastructure code in the domain layer. In this case, infrastructure code means something like "call that GET method of that REST service using this set of parameters in the query string". Obviously, this code will be in the 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 layer
  • DetectPriority interface - in the domain layer
  • InternalDetector in the domain layer
  • ExternalDetector in the infrastructure layer

And you don't need "business" names for your detectors then, because they are literally InternalDetector and ExternalDetector.

Upvotes: 4

Andreas Hütter
Andreas Hütter

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:

  • DetectPriorityInterface in your domain layer
  • InternalPriorityDetector (implementing DetectPriorityInterface) in your domain layer
  • ExternalPriorityDetector (implementing DetectPriorityInterface) in your infrastructure layer
  • PriorityDetectorFactoryInterface in your domain layer as well
  • PriorityDetectorFactory (implementing PriorityDetectorFactoryInterface) in your infrastructure layer
  • ...and the mentioned application service handling your use case in your application layer

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

VoiceOfUnreason
VoiceOfUnreason

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

Related Questions