Domain Driven Design and IoC/Dependency Injection

前端 未结 5 1763
借酒劲吻你
借酒劲吻你 2021-02-02 00:43

I\'m trying to apply now what I learned about DDD and I\'m a little bit confused about the flow of dependencies in the Domain model.

My questions are:

  1. Shou
相关标签:
5条回答
  • 2021-02-02 01:03

    Should an Entity be aware of Factories, Repositories, Services in the domain?

    An entity should never reference a repository or an application service. It is acceptable for entities to reference a factory if it uses it to create a constituent entity. It is also acceptable for an entity to have a dependency on a domain service if it uses that services for certain behavior.

    Should a Repository be aware of Services in the domain?

    Generally no. A repository should only be responsible for persistence.

    Now, in case I want to add a relation with a new tag. What would be the better way to do it?

    It depends on which layer you refer to. In a typical DDD architecture, you'd have both pieces of code. You'd have an article application service which encapsulates the domain and provides a granular method such as addTag where you'd pass an article ID and a tag ID. This method would retrieve the appropriate article and tag instances (if needed) and then:

    $article->tags->add(TagEntity);
    $articleRepository->save($article);
    

    All outer layers depending on this domain would communicate with it through the application service.

    0 讨论(0)
  • 2021-02-02 01:03

    Should an Entity be aware of Factories, Repositories, Services in the domain?

    • Application services: no
    • Domain services: yes, because they are in the domain layer
    • Factories: yes, because they are in the domain layer
    • Repository interfaces: yes, because they are in the domain layer
    • Repository implementations: no, because they are in the infrastructure layer

    Notice the differente between interface and implementation: that's why you should use interface & implementations.

    And FYI, Factories and Repositories are services after all, so you can generalize:

    • Service interfaces: yes if they are in the domain layer

    Easy

    A domain service is one which is defined within the domain layer, though the implementation may be part of the infrastructure layer. A repository is a domain service whose implementation is indeed in the infrastructure layer, while a factory is also a domain service whose implementation is generally within the domain layer.

    (Source: http://www.methodsandtools.com/archive/archive.php?id=97p2)

    Should a Repository be aware of Services in the domain?

    Usually no, why would it after all? The repository manage persistence. But I don't think that's "prohibited" because the infrastructure layer (persistence) knows about the domain layer.

    Another thing that is bothering my mind is how to treat to collections when I want to add and entity to the collection.

    Prefer the OOP approach when possible:

    $article = new Article();
    $article->addTag($tag);
    $articleRepository->save($article);
    

    I makes way more sense.

    a domain service is any business logic that does not easily live within an entity.

    (http://www.methodsandtools.com/archive/archive.php?id=97p2)

    or also:

    When a significant process or transformation in the domain is not a natural responsibility of an ENTITY or VALUE OBJECT, add an operation to the model as a standalone interface declared as a SERVICE.

    (Eric Evans)

    To sum up, create a domain service if you feel you need to, this is not automatic.

    0 讨论(0)
  • 2021-02-02 01:13

    First thing first, don't get to hung up on it - its easy to over engineer by creating unnecessary service classes etc. Less code is good. Check out the original source material, and the code in either Java or C#.

    1) Should an Entity be aware of Factories, Repositories, Services in the domain?

    It can if required. For example my (java) class annotated with @Entity can also be annotated with @Configurable and have a session/other classes injected into it. That is the point - to encapsulate all necessary business logic and expose a clear simple api located on one Domain class.

    2) Should a Repository be aware of Services in the domain?

    No. But the opposite is likely, a Service would use a repository.

    Services are used when more than one domain object/entity/root aggregate is utilized. So assuming a TAG is a separate entity, this would be fine :

    $articleService->addTag($article, TagEntity); 
    

    However if Tag is anot another root aggregate, you could just do

    $article->tags->add(TagEntity);
    

    And article itself does the save (without any other calls) by having a repository/dao injected inside it.

    0 讨论(0)
  • 2021-02-02 01:22

    Entities and Value Objects should never be dependent on anything except for each other. These are the most fundamental of all the building blocks of DDD. They represent the concepts of Your problem domain and thus should focus on the problem. By making them dependent on Factories, Repositories and Services You make that focus blur. There is another problem with having a reference to Services in Entities and Value Objects. Because Services also possess the domain logic You will be tempted to delegate some of the responsibilities of the domain model to Services which eventually may lead to the Anemic Domain Model.

    Factories and Repositories are just helpers used for creation and persistence of Entities. Most of the time they just have no analogy in the real problem domain, so having a reference from Factories and Repositories to Services and Entities would make no sense according to the domain logic.

    Regarding the example You provided, this is how I would implement it

    $article->addTag($tag);
    $articleRepository->save($article);
    

    I wouldn't give the direct access to the underlying collection, because I might want the Article to perform some domain logic (imposing constraints, validating) on the Tag before adding it to the collection.

    Avoid this

    $articleService->addTag($article, $tag);
    

    Use Services only to perform operations that don't belong to any Entity conceptually. First, try to fit it to an Entity, make sure it doesn't fit any. And only then use a Service for it. This way You won't end up with anemic domain models.

    UPDATE 1

    A quote from Eric Evans's "Domain-Driven Design: Tackling Complexity in the Heart of Software" book:

    SERVICES should be used judiciously and not allowed to strip the ENTITIES and VALUE OBJECTS of all their behavior.

    UPDATE 2

    Somebody has downvoted this answer and I don't know why. I can only suspect the reason. It could be about the references between Entities and Services or it could be about the example code. Well, I can't do much about the example code, because it was my opinion based on my own experience. But, I did some more research on the references part and here is what I came up with.

    I am not the only one who thinks that referencing Services, Repositories and Factories from Entities is not a good idea. I found similar questions here in SO:

    • Is it ok for entities to access repositories?
    • DDD - Dependecies between domain model, services and repositories
    • DDD - the rule that Entities can't access Repositories directly
    • DDD Entities making use of Services

    There are also some good articles on the subject, especially this one How not to inject services in entities which also presents a solution, if You desperately need to call a Service from Your Entity, named Double Dispatch. Here is an example from the article ported to PHP:

    interface MailService
    {
        public function send($sender, $recipient, $subject, $body);
    }
    
    class Message
    {
        //...
        public function sendThrough(MailService $mailService)
        {
            $subject = $this->isReply ? 'Re: ' . $this->title : $this->title;
            $mailService->send(
                $this->sender, 
                $this->recipient, 
                $subject, 
                $this->getMessageBody($this->content)
            );
        }
    }
    

    So, as You can see You don't have a reference to the MailService service inside Your Message entity, instead it's passed as an argument to the entity's method. The same solution is proposed by the author of this article "DDD: Services" at http://devlicio.us/ in the comments section.

    I have also tried to look at what Eric Evans says about this in his "Domain-Driven Design: Tackling Complexity in the Heart of Software" book. Having briefly searched, I didn't find the exact answer, but I found an example where an Entity actually calls the service statically, that is without having a reference to it.

    public class BrokerageAccount {
        String accountNumber;
        String customerSocialSecurityNumber;
    
        // Omit constructors, etc.
    
        public Customer getCustomer() {
            String sqlQuery =
                "SELECT * FROM CUSTOMER WHERE" +
                "SS_NUMBER = '" + customerSocialSecurityNumber + "'";
            return QueryService.findSingleCustomerFor(sqlQuery);
        }
    
        public Set getInvestments() {
            String sqlQuery =
                "SELECT * FROM INVESTMENT WHERE" +
                "BROKERAGE_ACCOUNT = '" + accountNumber + "'";
            return QueryService.findInvestmentsFor(sqlQuery);
        }
    }
    

    And the note below states the following:

    Note: The QueryService, a utility for fetching rows from the database and creating objects, is simple for explaining examples, but it's not necessarily a good design for a real project.

    If You have a look at the source code of the DDDSample project I've mentioned above, You'll see that Entities don't have any reference to anything except for the objects in the model package, i.e. Entities and Value Objects. By the way, the DDDSample project is described in the "Domain-Driven Design: Tackling Complexity in the Heart of Software" book in detail...

    Also, one more thing I would like to share with You is a similar discussion on the domaindrivendesign Yahoo Group. This message from the discussion quotes Eric Evans on the subject of model objects referencing Repositories.

    Conclusion

    To summarize, having a reference to Services, Repositories and Factories from Entities is NOT good. This is the most accepted opinion. Even though Repositories and Factories are citizens of the Domain layer, they are not part of the problem domain. Sometimes (e.g. in the Wikipedia article on the DDD) the concept of the Domain services is called Pure Fabrication which implies that the class (Service) "does not represent a concept in the problem domain". I'd rather refer to Factories and Repositories as Pure Fabrications, because Eric Evans does say something else in his book about the concept of Services:

    But when an operation is actually an important domain concept, a SERVICE forms a natural part of a MODEL-DRIVEN DESIGN. Declared in the model as a SERVICE, rather than as a phony object that doesn't actually represent anything, the standalone operation will not mislead anyone.

    According to the above-said, sometimes calling a Service from Your Entity might be a sane thing to want. Then, You can use the Double Dispatch approach, so that You won't have to keep a reference to the Service in Your Entity class.

    Of course, there are always some people who disagree with the mainstream opinion like the author of Accessing Domain Services from Entities article.

    0 讨论(0)
  • 2021-02-02 01:24

    I'll answer this with the precondition that I don't think there is a right answer, just different approaches.

    Thinking in terms of domain objects I would say the first approach is DDD. You are dealing purely with domain objects.

    I do think however there are uses for a service object that exposes this as part of an API/Service layer. In this case you would wrap your code from #1 within your service #2. This allows you to avoid exposing your domain objects to external consumers - and you can keep the external interface/API unaltered while updating your domain model.

    This however is just one opinion.

    0 讨论(0)
提交回复
热议问题