This is a language independent question but I write the sample codes in PHP.
We have a two classes :
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 :
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());
}
}
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;
}
}
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?
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,
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With