Tuesday 7 August 2018

DDD: domain driven design and dependency injection

I agree that 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.
An example is for the entity class User, we define a method which has a argument of a service instance:

    public interface UserService
    {
        List<User> findSameLastName(string lastName);
    }

    public class User
    {
        private String lastName;
        //...

        public List<User> findByLastName(UserService userService)
        {
            return userService.findSameLastName(this.lastName);
        }
    }

We do not need to inject IUserService into the User model but introduced as a method argument, because normally this method is invoked by upper layer, say UI layer controller, if we user Spring Framework, in controller we can autowire the userService and then call findByLastName method provide the service instance:
@RestController
public UserController {
    @Autowired
    private UserService userServiceImpl;
    // ...
    @RequestMapping(...)
    private void aMethod(){
    // say we have a user instance from Request...
   List<User> users = user.findByLastName(userServiceImpl); // here is the magic :)
    }
}