Is Doctrine's EntityManager a service locator?

Well in my current application, my front controller class is passing each repository and service classes to the constructor of my application controller classes. It works fine for simple controllers, but can quickly become complicated. An example of such application controller constructor looks like this.

class UserProfileController extends ApplicationController{
    
    private $userProfileRepository;
    private $contactRepository;
    private $authenticationService;    

    public function __construct(UserRepository $userRepository, UserProfileRepository $userProfileRepository, ContactRepository $contactRepository, AuthenticationService $authenticationService){
            parent::__construct($userRepository);
            $this->userProfileRepository = $userProfileRepository;
            $this->contactRepository = $contactRepository;
            $this->authenticationService = $authenticationService;
        }
}

The fact is that the user profile pages need to display a lot of information, and it needs several repositories to complete the task(I actually leave out Posts, Comments and other repositories to make it less scary, but its still a lot). Its very inconvenient, and the constructors of these controller classes can be easily polluted if multiple repositories and services need to be injected. Also the UserProfileRepository and ContactRepository may need to eager load User object using UserRepository, this can quickly become a royal mess.

I know that in Doctrine, however, there is an EntityManager sitting on top of the repositories. With Doctrine, you can simply pass an EntityManager class to the constructor of controllers, and it will be able to create any repositories given that they are valid. Below is how the code can be refactored using Doctrine’s EntityManager:

class UserProfileController extends ApplicationController{
    
    private $authenticationService;    

    public function __construct(EntityManager $entityManager, AuthenticationService $authenticationService){
            parent::__construct($entityManager);
            $this->authenticationService = $authenticationService;
        }
}

The controller’s constructor now looks a lot cleaner, and this way if I want to get a list of users, user profiles and their contact information, I can just call the methods on entity manager to retrieve the corresponding repositories:

$userRepository = $this->entityManager->getRepository("UserRepository");
$userProfileRepository = $this->entityManager->getRepository("UserProfileRepository");
$userProfileRepository = $this->entityManager->getRepository("ContactRepository");

From first glance, it looks elegant and the EntityManager can encapsulate dependencies between repositories too. However, I cant help but notice that the entity manager is now effectively acting like a service locator. Is it just me or anyone else feels this way too? Since we all know that service locator is an anti-pattern, and we should rely on Dependency Injection. Does this mean that Doctrine’s Entity Manager is not a good thing to use despite the fact that it does make things simpler? What do you think?

I’d say, the Entity Manager is just that, a manager of entities. Entities aren’t services really. The EM itself, is a service. And theoretically, you could also add it to a DI container like in Symfony’s service container and it is then automagically available in any controller, which extends the base Controller, making the EM’s usage even more convenient.

class UserProfileController extends Controller
{
    public function indexAction()
    {
        $em = $this->get('doctrine')->getManager();
        // do what you want with $em
    }
}

Scott

Yes, the entity manager is acting as a service locator for repositories.

But do you really need all those repositories for a UserProfileAction? Domain Driven Design has the notion of bounded contextes. Within a given context, one business object is the root and controls the others. So for a UserProfileContext, the UserProfile object is the root and the UserProfileRepository should have all the functionality need to deal with persisting not only the UserProfile but any of it’s children as well. In other contextes, the UserProfile object will just be a child object.

Keep in mind that in Doctrine, all repositories share the same entity manager so it’s easy enough to define a query in the UserProfileRepository that reaches out and grabs it’s nested objects.

The authentication service is somewhat different. We can talk about it later if you want.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.