3

This is a language independent question but I write the sample codes in PHP.

We have a two classes :

  1. User class
  2. UserRepository class

UserRepository class deals with the interaction with DB and loading the required User in to a User object and return it.

Within the User class let's say there is an email attribute which is not loaded in the first place, and it would be lazy-loaded from Database whenever it's needed. But User class should not be aware of the UserRepository class, so we create a email proxy here. Which is aware of the UserRepository and wheveer the email is needed it would ask it from the UserRepository.

So here is the Code so far :

User class

class User
{
    private $username;
    private $email_proxy;
    public function __construct($username,EmailProxy $email_proxy)
    {
        $this->username = $username;
        $this->email_proxy = $email_proxy;
    }

    public function get_username()
    {
        return $this->username;
    }

    public function get_email()
    {
        return $this->email_proxy->get_email($this->get_username());
    }
}

UserRepository class

class UserRepository
{
    private $db;
    public function __construct($db)
    {
        $this->db = $db;
    }

    /**
     * @param string username
     * @return string email , email address of the user
     */
    public function get_email_by_username($username)
    {
        // interaction with DB, ...
        return $user_email;
    }
} 

EmailProxy class

class EmailProxy
{
    private $user_repository;
    public function __construct(UserRepository $repository)
    {
        $this->user_repository = $repository;
    }
    public function get_email($username)
    {
        return $this->user_repository->get_email_by_username($username);
    }
}

And here is the usage sample :

$user_repository = new UserRepository($db_instance);
$email_proxy = new EmailProxy();
$user = new User('my_username', $email_proxy);

So far so good. But here is the tricky part which I need your help about. By it's nature, a UserRepository should be responsible for fetching a User object from DataBase, constructing a User object and returning it. Like below :

class UserRepository
{
    private $db;
    public function __construct($db)
    {
        $this->db = $db;
    }

    public function get_email_by_username($username)
    {
        // interaction with DB, ...
        return $user_email;
    }

    public function get_user_by_username($username)
    {
        // fetch the User from DB and ...
        return new User($fetched_username) // missing the EmailProxy
    }
}

So my question is how do pass the EmailProxy to the User object which is created by UserRepository ?

  • Do you inject the UserProxy instance to UserRepository so you could inject it to newly created User objects ?

  • Would you use a Dependency Injection Container for this?

  • Would you use a factory?

EDIT

EmailProxy is already aware of UserRepository, if we pass EmailProxy to UserRepository as well, it would be a circular dependency.

Any code/comments would be appreciated.

Thanks,

Majid
  • 2,599
  • 3
  • 13
  • 14

2 Answers2

1

It's a tricky question and I'd love to ear how people handle it. In the mean time, here's what I've come up to myself.

I would put the creation of the User and UserRepository classes in a Factory

class ServiceFactory
{
    private $instances=[];
    public function createUser($data)
    {
        return new User($data)
            ->setEmailProxy($this->createEmailProxy());

    }

    public function createEmailProxy()
    {
        if (!isset($this->instances['EmailProxy'])) {
            $this->instances['EmailProxy'] = new EmailProxy(
                $this->createUserRepository()
            );
        }
        return $this->instances['EmailProxy'];
    }

    public function createUserRepository()
    {
        if (!isset($this->instances['UserRepository'])) {
            $this->instances['UserRepository'] = new UserRepository(
                $db, 
                $this->createDtoFactory()
               );
        }
        return $this->instances['UserRepository'];
    }

    public function createDtoProvider()
    {
        return new DtoProvider($this);
    }

}

Here's the code of the DtoProvider class

class DtoProvider
{
    private $serviceFactory;

    public function __construct($factory) 
    {
        $this->serviceFactory = $factory;
    }

    public function createUser($data)
    {
        return $this->serviceFactory->createUser($data);
    }

    public function createOtherStuff($data)
    {
        return $this->serviceFactory->createOtherStuff($data);
    }
}

The purpose of this class is hide the factory(ies) from the services. You only provide them what they need.

Now your UserRepository should look something like that

class UserRepository
{
    private $db;
    private $services;
    public function __construct($db, $serviceProvider)
    {
        $this->db = $db;
        $this->services = $serviceProvider;
    }

    public function get_email_by_username($username)
    {
        // interaction with DB, ...
        return $user_email;
    }

    public function get_user_by_username($username)
    {
        // fetch the User from DB and ...
        return $this->services->createUser($data)
    }
}

It seems a bit overkill (and it propably is) but it allows you to refactor your code quite easyly which is good for long term application.

I guess it all depends whether you prefer code that is easy to read or code that is easy (quick) to write.

Alfwed
  • 3,239
  • 2
  • 15
  • 20
  • Thanks for your great reply. It for sure works great and decouple the dependencies. As you said it's a bit overkill though. – Majid Apr 05 '13 at 21:22
  • 1
    I guess it depends whether you prefer code that is easy to read or code that is easy (quick) to write... :) – Alfwed Apr 06 '13 at 00:28
0

Perhaps a reference to the User object inside your UserRepository::get_email_by_username() method.

Something like:

public function get_email_by_username(&$username)
{
    // interaction with DB, ...
    $username->setEmail($email_proxy);
}

http://php.net/manual/en/language.references.pass.php


In reply to your comment, you can simply call the UserRepository method when you create the user:

public function get_user_by_username($username)
{
    $email_proxy = this->get_email_by_username($username);
    return new User($fetched_username, $email_proxy);
}

However, that seems too obvious an answer and I suspect you're asking something entirely different than is posted. You may want to rephrase your question:

So my question is how do pass the EmailProxy to the User object which is created by UserRepository ?

Cypher
  • 2,079
  • 2
  • 23
  • 38
  • Thanks for the reply. But my question is how UserRepository should be aware of the EmailProxy? So when it's creating a new user in "get_user_by_username" it could pass it along. – Majid Apr 05 '13 at 14:22
  • "UserRepository->get_email_by_username()" returns an email string, and not the email_proxy. UserRepository doesn't have access to "EmailProxy". The problem is EmailProxy is aware of UserRepository, as it uses it for fetching the email. If we pass EmailProxy instance to UserRepository as well, it would be a circular dependency. – Majid Apr 05 '13 at 21:05