How should domain services access infrastructure services?

1) When the domain level uses the IS infrastructure service, then its interface is defined within the domain level, and its implementation is defined at the infrastructure level.

We should not enter IS (for example, repository or email service) directly in the domain entities:

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

Instead, if any Domain Entity method needs a specific IS , then the Application layer can pass that IS as an argument to this method:

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

a) I suggest that IS should also not be entered 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 Domain Service methods that intend to use it:

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

b) I assume that if the Domain Entity method needs to use a domain service, it does not need to get it with an argument (although passing the Domain Service as an argument has the advantage of explicitly passing the method dependencies), but can instead call it directly, and the reason is in that both the domain entity and the domain service are domain concepts:

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

UPDATE:

one)

IS can be entered into the domain service if it is necessary for functionality - i.e. not passed to the method. This is different from and this is due to the fact that the domain service is stateless, and therefore it can be configured with the necessary dependencies once and used where necessary, for example, by other objects.

a) Therefore, the main reason why IS should not be entered into domain objects is due to their statefulf state?

b) But I thought that the main reason not to enter IS in Domain Entities is because it violates the rule of maintaining ignorance?

c) What problems cause the natural character of the object if we insert it using IS ?

d) Does the IS introduce a PI violation into the domain service? If not, why not?

2)

The domain service should be passed to the object as an argument, like you will pass the repository interface as an argument. The same principles apply.

But unlike the Repository, a domain service is a domain concept, so what do you mean by "the same principles"? Namely, entering a domain service into a domain entity will not violate PI , while investing in a repository will!

SECOND UPDATE:

one)

but)

This is one of the reasons. Another is that a useless connection is created. If the repository is necessary for only one behavior on the object, why introduce it into the entity all the time? Also, now you need to think about how you will resolve these dependencies? Make entities part of a dependency injection schedule? This quickly overloads the responsibilities of the facility.

So, if I understand you correctly - Injecting IS into a DS domain service does not violate SRP, because DS uses it to perform its assigned tasks (that is, its assigned responsibility), and injecting IS into a domain entity is a violation of SRP, since the primary responsibility of Domain Entity is focused on its life cycle and identity, and IS in most cases is not an integral part of managing these two tasks (i.e. focuses on the life cycle and identity)?

b)

You can still pass IS to the domain entity method and the problem here would not be a PI violation, since you are passing an interface, not an implementation. The problem is a PSA violation if the domain method used only one method on the IS interface.

I - But in several of your previous posts you noted that it is normal to pass IS as an argument to the Domain Entity method, but here you say that it will violate SRP if this domain method used only one method for an IS instance?

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

d)

PI is supported using interfaces.

I read several times that even if a domain method refers to a repository through an interface, it is still considered a violation of the PI. Why do not you agree with this?

2)

It is better, however, to be very explicit. Therefore, instead of a repository and an implicit understanding of what a service is for the subject, declare the provided functions as their own interface and depend on this object.

a) So, the only reason not to enter a domain service into a domain entity due to SRP violation?

b)

declare the provided functionality as your own interface

I assume that you propose to use role interfaces? But not even inserting a role-based interface (implemented, say, by Domain Service) into a domain entity causes an SRP violation, because, as you noted in 1a , injection functionality is most likely needed only for a single domain entity behavior ?!

It looks like you and Aaron Hawkins are on opposite sides with respect to transferring IS methods to domain entities ?!

THIRD UPDATE:

one)

but)

So, if I understand you correctly - Putting IS into the DS domain service does not violate SRP, because DS uses it to perform the assigned tasks (that is, its assigned responsibility), while the injection of IP into the Domain Essence is a violation of the PSA, since the main Domain Entity responsibility focuses on its life cycle and identity, and ISmost time is not an integral part of managing these two tasks (i.e. focusing on the life cycle and personality)?

Yes, this is correct and one of the main reasons.

I - from a distance it seems quite reasonable that, by introducing IS into the DE domain entity, this DE will violate SRP, because IS will not facilitate the management of the two tasks assigned to DE .

But it is a little more complicated when you try to present this scenario in more detail. Namely, if DE methods are focused on managing two assigned tasks (that is, on its life cycle and identity), then if one of these methods needs an IS , is it reasonable to assume that an IS is required to complete the two assigned tasks, and not some other task not related to the DE life cycle. If so, how can we argue that DE violates SRP?

II - It is also difficult for me to imagine what exactly is meant by the management of the DE life cycle. For starters, when an identifier is assigned to DE , that name does not change. So, what about his identity, we would need to manage?

III - And what is meant by DE life cycle management? Perhaps defining invariants on DE that save their data or ...?

IV. Thus, all other tasks (that is, those that are not related to the life cycle and identity of DE) that the real world performs must be accounted for from DE and placed in related objects?

d)

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

This is not scary, but it can violate SRP or, as guillaume31 - ISP is more clearly stated.

I'm not sure how we can argue that injecting IS into DE can violate ISP , because as far as I know, ISP can only be violated by an object that implements this interface, and not by the object into which the implementation of this interface is injected?

FOURTH UPDATE:

I'm starting to realize that SRP is more confusing than I originally thought

but)

The behavior associated with the object, which usually entails a state of change, should also be placed in the object. If this behavior requires the use of a service, pass on the service, but as a rule, try to put as much behavior as possible into the object.

IV - 1 The following behavioral methods do not include state changes, but I believe that they should also belong to the Dog object:

 class Dog { ... void DoBark(); void DoFetch(); void DoGuard(); Breed GetBreed(); Pedigree GetPedigree(); Snack FavSnack(); } 

IV - b) Are the GetBreed , GetPedigree and FavSnack behavioral? If so, then the properties Breed , Pedigree and Snack should also be considered as behavior, since they essentially provide the same functionality (provided that GetBreed , GetPedigree and FavSnack do not perform some heavy calculations, but simply return objects):

 class Dog { ... void DoBark(); void DoFetch(); void DoGuard(); Breed Breed { get{...} } Pedigree Pedigree { get{...} } Snack Snack { get{...} } } 

IV - c) if the above properties also had setters, will we say that they contain changing state behavior?

IV - d)

The behavior associated with the object, which usually entails a state of change, should also be placed in the object.

But if the primary responsibility of Domain Entity is to manage its life cycle, it will not include non-life cycle management behavior that is a violation of SRP (in the example above, methods like Dog.DoBark most likely do not have much in common with Dog life cycle) ?!

d)

I.

Passing IS to the DE behavioral method is better, however, it can violate SRP / ISP if the IS interface has a lot of things unrelated to behavior at hand. This is the basic premise of ISP - dependencies must be made on specific interfaces, in contrast to bloated interfaces, which usually contain the required functionality.

therefore, if IS , passed as an argument to one of the DE-methods of behavior, has some operations that are not related to behavior, but the DE M method does not use any of the IS methods, not related to the behavior of M , should be processed, we still consider Does DE violate SRP / ISP?

II. - I understand what you are saying, but my confusion stems from the fact that according to the following ISP definitions, this term should be used only to mean that the ServObj object that implements the specific interface violates the ISP, and the object into which ServObj is entered , violates SRP (due to receiving ServObj ):

The principle of interface segregation is similar to Single. The principle of responsibility is that they relate to cohesion of duty. In fact, ISP can be understood as applying SRP to a common object interface.

To a certain extent, the ISP can be considered a subset or more specific form of the principle of shared responsibility. The ISP shift perspective, however, considers a public API for a given class or module.

Thank you

+7
source share
2 answers

1a). IS can be entered into the domain service if it is necessary for functionality, i.e. not passed to the method. This differs from entities, and this is due to the fact that the domain service has no status and therefore can be configured with the necessary dependencies once and used where necessary, for example, by other objects.

1b) The domain service must be passed to the entity as an argument, since you will pass the repository interface as an argument. The same principles apply. In addition, passing the entire interface of the repository can create a useless connection, and therefore it is better to declare and transmit a specific specific interface.

UPDATE

1a) This is one of the reasons. Another is that a useless connection is created. If the repository is needed for only one behavior in the entity, why introduce it into the entity all the time? Also, now you need to think about how you will resolve these dependencies? Make objects part of dependency injection schedule? This quickly overloads the responsibilities of the facility.

1b) Violation of PI is an example of a more general concept of violation of the principle of single responsibility. You can still pass IS to the domain entity method, and the problem here is not a PI violation, as you are passing an interface, not an implementation. The problem is SRP violation if the domain method used only one method on the IS interface.

1c) See 1a)

1d) No, because PI is supported using interfaces.

2) Yes, so I suggest avoiding direct access to the repository interfaces. The principles are similar, since you can consider both the repository interface and the domain service as abstract services that provide some behavior that is interesting for the domain object. It is better, however, to be very explicit. Therefore, instead of transferring the repository and implicitly understanding that it provides an entity service, declare the provided functionality as your own interface and depend on this object.

UPDATE 2

1a) Yes, this is correct and one of the main reasons.

1b) This is not scary, but it can violate SRP or more clearly indicate guillaume31 - ISP . Therefore, it is better than injecting an entity into an instance, but it can be improved by declaring a specific interface.

And no, if you are creating a role-based interface, then this is as good as in this scenario.

1d) If an object uses the repository interface to save, this is a PI violation. However, if it uses the repository interface to execute some query in order to run its business logic, we are not really talking about persistence. And to really split the repository, use the role-specific interface instead.

2a) Yes, for the same reason that it is not recommended to enter the repository into the entity.

2b) Yes, but I only assume that in the case when you transfer the domain service to the behavior method on the entity without inserting it into the object instance itself.

UPDATE 3

1a) This may be controversial. I agree. However, from a practical point of view, it is better to design DEs so that their dependencies on external services are either eliminated or made very explicit and isolated. If your DE is instance dependent on IS, this means that whenever an instance of DE is created, for example, during recovery or when creating a new object, IS must be provided at that time. This complicates the dependency graph. Moreover, if the DE is responsible for maintaining the integrity of its own state, why is there a dependency on external service? It may be necessary for the service to cause any behavior, but to maintain integrity, it can usually be performed without external services. In fact, dependencies on external services in this way are usually the smell of folded responsibilities.

II, III) This includes such things as the protection of invariants. For example, an object can ensure that its values ​​do not fall into an inconsistent state, throwing an exception. It is a fundamental premise of OOP to encapsulate state and detect behavior.

Iv). Behavior associated with an object, which usually entails a change in state, should also be placed in the object. If this behavior requires the use of a service, pass this service on, but usually try to put as much behavior as possible into the object. It will not always be perfect, but you can strive for the ideal.

d) To be sure that when I say β€œinjection”, I am referring to the reference to the required instance of the IP from DE. This is rejected for the reasons above. Passing IS using the DE behavior method is better, however, it can violate SRP / ISP if the IS interface has many things not related to behavior. This is the basic premise of ISP dependencies to be made on specific interfaces, rather than on bloated interfaces that contain the required functionality.

+6
source

Domain entities should not know anything about persistence (repositories) or processes (domain services). Instead, your application will combine them.

Here's how to organize repository orchestration, for example:

 ISomeAggregateRepository repository = //TODO: Construct repository and load it with the de-hydrated aggregates you intend to work with under your scope. foreach (SomeAggregate someAggregate in repository) //The repository functions like an in memory collection. { someAggregate.DoSomethingCool(); //The domain aggregate would expose an event to let the repository know that this method has been called and that the instance should be saved afterwards. (mimicking saving to a reference) } 

For example, organizing a domain service orchestration:

 ISomeDomainService service = new ConcreteInfrastructuralSpecificDomainService(); service.DoSomethingCool(SomeAggregate target); //Send the aggregate to the domain service, not the other way around. 

I was on the same path as you, for the longest time, and was very confused, but as soon as I understood how to organize the code in the way my examples are depicted, everything became much clearer.

Just remember that the application should be what chooses the implementation (usually infrastructure-specific) of your repositories, domain services, and possibly your work unit.

Hope this helps!

UPDATE

A - Do you mean only the application level should create and use domain services, while domain entities should not even know that they exist? But why?

Here are some of them that I came up with from my head. I would not consider this a complete list, but should be sufficient to justify my statement.

  • Encapsulation (the internal work of domain aggregates is not subject to the implementation of future transferred repositories and / or domain services)

  • Repositories / Domain Services are an integral infrastructure and therefore should not be combined into your business domain, which keeps your business logic clean and expressive. (Interfaces are in the domain to provide executors with a contractual way to implement repositories / domain services, preventing the application from needing to link to more projects than necessary.)

  • If your domain is not aware of domain repositories / services, it further enhances the decoupling and possible dependency that will go in the wrong direction (infrastructure project should refer to the domain project, and not vice versa, what could happen if someone implements your domain without dependency injection by passing in a specific implementation).

  • Change Management / Maintaining Health (see Onion Architecture )

B - I assume that all the code you posted is at the application level?

You are right sir!

+3
source

All Articles