Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should Domain Services call Infrastructure services?

1) When Domain layer is using an Infrastructure Service IS, then its interface is defined within a Domain layer, while its implementation is defined within an Infrastructure layer.

We shouldn't inject IS ( such as a Repository or E-mail service) directly into Domain entities:

class Foo
{
       IRepository repo;
           ...
       public int DoSomething()
       {
            var info = repo.Get...;
              ...
       }
}

Instead, if a some method of a Domain Entity needs a particular IS, then Application layer could pass that IS as an argument to that method:

 class Foo
{
           ...
       public int DoSomething(IRepository repo)
       {
            var info = repo.Get...;
              ...
       }
}

a) I assume IS also shouldn't be injected directly into Domain Services:

class TransferService
{
         IRepository repo;
             ...
         public bool Transfer()
         {
            var info = repo.Get...;
              ...
         } 
} 

, but instead IS should be passed as an argument to those methods of Domain Service which intend to use it:

class TransferService
{
         public bool Transfer(IRepository repo)
         {
            var info = repo.Get...;
              ...
         } 
} 

b) I assume if a method of Domain Entity needs to use a Domain Service, it doesn't need to receive it via an argument ( though passing Domain Service as an argument has a benefit of explicitly conveying method's dependencies ), but instead can call it directly, reason being that both Domain Entity and Domain Service are both domain concepts:

class Foo
{
       ...

       public int DoSomething()
       {
            var info = TransferService.Transfer(...);
              ...
       }
}

UPDATE:

1)

An IS can be injected into a domain service if it needs it for functionality - ie not passed to method. This is different than entities and it is because a domain service is stateless and so it can be configured with required dependencies once and used where needed, such as by other entities.

a) So major reason why IS shouldn't be injected into Domain Entities is due to their statefulf nature?

b) But I thought primary reason for not to injecting IS into Domain Entities is because it would violate Persistence Ignorance rule?

c) What problems does statefull nature of an entity cause if we inject it with IS?

d) Doesn't injecting IS into Domain Service violate PI? If not, why not?

2)

A domain service should be passed to entity as argument much like you would pass a repository interface as argument. The same principles apply.

But unlike Repository a Domain Service is a domain concept, so what do you mean by "same principles apply"? Namely, injecting Domain Service into Domain Entity wouldn't violate PI while injecting a Repository would!

SECOND UPDATE:

1)

a)

That is one reason. Another is that is creates needless coupling. If a repository is needed for only a single behavior on the entity, why inject it into entity all the time? Also, now you have to consider how you will resolve those dependencies? Make entities part of dependency injection graph? This quickly overloads the responsibilities of the entity.

So if I understood you correctly - Injecting IS into Domain Service DS doesn't violate SRP, because DS is using it to execute its designated tasks ( ie its designated responsibility), while injecting IS into Domain Entity is a violation of SRP because primary responsibility of Domain Entity is focusing on its life cycle and identity, and IS most of the times isn't an integral part in managing these two tasks ( ie focusing on life cycle and identity )?

b)

You can still pass an IS to a domain entity method and the problem here wouldn't be violation of PI since you're passing an interface, not an implementation. The problem would be violation of SRP if the domain method only used one method on the IS interface.

I - But in several of your previous posts you noted that it's ok to pass IS as an argument to a method of Domain Entity, but here you're saying that it would violate SRP if this domain method used only one method on IS instance?

II - If IS implemented a role-based interface containing a single method, and instead we pass this role-based interface as an argument to domain method, would you still consider this as a violation of SRP? If not, why not?

d)

PI is maintained with the use of interfaces.

I've read several times that even if domain method is referencing a repository via an interface, it's still considered a violation of PI. Why don't you agree with that?

2)

It is better however to be very explicit. So instead of passing a repository and implicitly understanding that it happens to provide a service to the entity, declare the provided functionality as its own interface and have the entity depend on that.

a) So single reason for not injecting Domain Service into Domain Entity is due to violation of SRP?

b)

declare the provided functionality as its own interface

I assume you're suggesting to use role-based interfaces? But wouldn't even injecting a role-based interface ( implemented by say Domain Service ) into Domain Entity cause violation of SRP, since as you noted in 1a, the injected functionality would most probably be needed only for a single behavior of Domain Entity?!

It appears you and Aaron Hawkins are on the opposite sides with regards to passing IS to methods of Domain Entities?!

THIRD UPDATE:

1)

a)

So if I understood you correctly - Injecting IS into Domain Service DS doesn't violate SRP, because DS is using it to execute its designated tasks ( ie its designated responsibility), while injecting IS into Domain Entity is a violation of SRP because primary responsibility of Domain Entity is focusing on its life cycle and identity, and ISmost of the times isn't an integral part in managing these two tasks ( ie focusing on life cycle and identity )?

Yes that is correct and one of the primary reasons.

I - From the distance it seems perfectly reasonable how by injecting IS into Domain Entity DE, this DE would violate SRP, because IS won't contribute to managing the two task designated to DE.

But it's a bit harder when trying to imagine this scenario in more detail. Namely, if methods of DE are focused on managing the two designated task ( ie its life cycle and identity ), then if one of these methods needs IS, wouldn't it be reasonable to assume it needs IS to accomplish the two designated task and not some other task unrelated to DE's life cycle and identity? If yes, then how could we claim DE violates SRP?

II - I also have hard time imagining what exactly is meant by managing DE's life cycle and identity. For starters, once DE is assigned an identity, this identity doesn't change. So what about its identity would we need to manage?

III - And what is meant by managing DE's life cycle? Perhaps defining the invariants on DE which keep its data consistent or ...?

IV - So all other tasks ( ie those that aren't related to DE's life cycle and identity ) that real world entity performs should be factored out from DE and put into related objects?

d)

If IS implemented a role-based interface containing a single method, and instead we pass this role-based interface as an argument to domain method, would you still consider this as a violation of SRP? If not, why not?

It isn't terrible to do it, but it has potential to violate SRP or as more clearly specified by guillaume31 - ISP.

I'm not sure how we can claim injecting IS into DE could violate ISP, since to my knowledge ISP can only be violated by an object that implements this interface and not an object into which an implementation of this interface is injected?

FOURTH UPDATE:

I'm beginning to realize SRP is way more confusing than I initially thought

a)

Behavior associated with the entity, which usually entails state changes, should also be placed into the entity. If such behavior requires use of a service, pass that service in, but generally try to place as much behavior into the entity as possible.

IV – 1 The following behavioral methods don't include state changes, but I gather they too should belong to a Dog entity:

class Dog
{
            ...
       void DoBark();
       void DoFetch();
       void DoGuard();

       Breed    GetBreed();
       Pedigree GetPedigree();
       Snack    FavSnack();
}

IV – b) Are GetBreed, GetPedigree and FavSnack behavioral methods? If yes, then properties Breed, Pedigree and Snack should also be considered as behaviors, since they essentially provide the same functionality ( assuming that GetBreed, GetPedigree and FavSnack don't do some heavy computation, but simply return the objects ):

class Dog
{
            ...
       void DoBark();
       void DoFetch();
       void DoGuard();

       Breed    Breed { get{...} }
       Pedigree Pedigree { get{...} }
       Snack    Snack { get{...} }
}

IV – c) if above properties also had setters, would we say they contain state-changing behavior?

IV – d)

Behavior associated with the entity, which usually entails state changes, should also be placed into the entity.

But if main responsibility of Domain Entity is managing its life cycle, then wouldn't including behavior not related to managing the life cycle be a violation of SRP ( in above example, methods such as Dog.DoBark most probably don't have much to do with Dog's life cycle)?!

d)

I.

Passing IS to a DE behavioral method is better, however can violate SRP/ISP if IS interface has lots of things unrelated to the behavior at hand. This is the basic premise of ISP - dependencies should be made on specific interfaces as opposed to bloated interfaces that happen to contain the required functionality.

so if IS passed as an argument to one of DE's behavioral methods does have some operations unrelated to behavior at hand, but DE's method M doesn't use any of IS's methods unrelated to behavior M should be handling, we still consider DE to be violating SRP/ISP?

II. – I understand what you're saying, but my confusion steams from the fact that according to the following definitions of ISP, the term should only be used to designate that an object ServObj implementing a particular interface is violating ISP, while object into which ServObj is injected is violating SRP ( due to receiving ServObj ):

The Interface Segregation Principle is similar to the Single Responsibility Principle in that both deal with the cohesion of responsibilities. In fact, the ISP can be understood as the application of the SRP to an object’s public interface.

To a certain extent, ISP can be considered a sub-set, or more specific form of the Single Responsibility Principle. The perspective shift of ISP, though, examines the public API for a given class or module.

thank you

like image 489
user437291 Avatar asked Jan 30 '13 20:01

user437291


1 Answers

1a) An IS can be injected into a domain service if it needs it for functionality - ie not passed to method. This is different than entities and it is because a domain service is stateless and so it can be configured with required dependencies once and used where needed, such as by other entities.

1b) A domain service should be passed to entity as argument much like you would pass a repository interface as argument. The same principles apply. Also, passing entire repository interface can create needless coupling and so it is better to declare and pass a role specific interface.

UPDATE

1a) That is one reason. Another is that is creates needless coupling. If a repository is needed for only a single behavior on the entity, why inject it into entity all the time? Also, now you have to consider how you will resolve those dependencies? Make entities part of dependency injection graph? This quickly overloads the responsibilities of the entity.

1b) Violation of PI is an instance of the more general concept of violating the single responsibility principle. You can still pass an IS to a domain entity method and the problem here wouldn't be violation of PI since you're passing an interface, not an implementation. The problem would be violation of SRP if the domain method only used one method on the IS interface.

1c) See 1a)

1d) No because PI is maintained with the use of interfaces.

2) Yes, which is why I would suggest avoiding passing repository interfaces directly. The principles are similar because you can think of both a repository interface and a domain service as abstract services that provide some behavior interesting for a domain entity. It is better however to be very explicit. So instead of passing a repository and implicitly understanding that it happens to provide a service to the entity, declare the provided functionality as its own interface and have the entity depend on that.

UPDATE 2

1a) Yes that is correct and one of the primary reasons.

1b) It isn't terrible to do it, but it has potential to violate SRP or as more clearly specified by guillaume31 - ISP. So it is better than say injecting into entity instance, but could be improved by declaring a specific interface.

And no, if you create a role-based interface then this is as good as it gets in this scenario.

1d) If an entity uses a repository interface to persist itself then this is a violation of PI. However, if it uses a repository interface to perform some query in order to run its business logic, then we're not really talking about persistence. And to really decouple the repository, employ a role specific interface instead.

2a) Yes, for the same reason that it isn't appropriate to inject repository into entity.

2b) Yes, but I'm only suggesting that in the scenario where you're passing a domain service to a behavioral method on the entity, not injecting it into the entity instance itself.

UPDATE 3

1a) This can be a point of contention I agree. However, from a practical point of view, it is better to design DEs such that their dependencies on external services are either eliminated or made very explicit and isolated. If your DE has an instance dependency on an IS, that means that whenever an instance of the DE is created, such as during reconstitution or when creating a new entity, the IS must be provided at that time. This complicates the dependency graph. Moreover, if a DE is responsible for maintaining the integrity of its own state, why would there be a dependency on an external service? There may be a need for a service to invoke some behavior, but to maintaining integrity can usually be accomplished without external services. In fact, dependencies on external services in this way is usually a smell of conflated responsibilities.

II, III) This includes such things as protecting invariants. For example, an entity could ensure that its values don't get into an inconsistent state by raising an exception. This is a fundamental premise of OOP - encapsulate state and expose behavior.

IV) Behavior associated with the entity, which usually entails state changes, should also be placed into the entity. If such behavior requires use of a service, pass that service in, but generally try to place as much behavior into the entity as possible. This won't always be perfect, but you can strive for the ideal.

d) To be sure, when I say injecting IS I mean having a required instance reference to an IS from a DE. This is discouraged for reasons above. Passing IS to a DE behavioral method is better, however can violate SRP/ISP if IS interface has lots of things unrelated to the behavior at hand. This is the basic premise of ISP - dependencies should be made on specific interfaces as opposed to bloated interfaces that happen to contain the required functionality.

like image 162
eulerfx Avatar answered Nov 07 '22 04:11

eulerfx