Domain Services and Infrastructure Services

It's common to encounter infrastructural dependencies when modeling a Domain Service  — for example, in the case where an authentication mechanism that handles password hashing is required. In this instance, you could use a Separated Interface, which allows for multiple hashing mechanisms to be defined. Using this pattern still provides you with a clear Separation of Concerns between the Domain and the Infrastructure:

namespace DddAuthDomainModel;

interface SignUp
{
public function execute($aUsername, $aPassword);
}

Using the preceding interface found in the Domain, we could create an implementation in the Infrastructure layer, like the following:

namespace DddAuthInfrastructureAuthentication;

class DefaultHashingSignUp implements DddAuthDomainModelSignUp
{
private $userRepository;

public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}

public function execute($aUsername, $aPassword)
{
if (!$this->userRepository->has($aUsername)) {
throw UserDoesNotExistException::fromUsername($aUsername);
}

$aUser = $this->userRepository->byUsername($aUsername);

if (!$this->isPasswordValidForUser($aUser, $aPassword)) {
throw new BadCredentialsException($aUser, $aPassword);
}

return $aUser;
}

private function isPasswordValidForUser(
User $aUser, $anUnencryptedPassword
) {
return password_verify($anUnencryptedPassword,$aUser->hash());
}
}

Here is another implementation based instead on the MD5 algorithm:

namespace DddAuthInfrastructureAuthentication;

use DddAuthDomainModelSignUp

class Md5HashingSignUp implements SignUp
{
const SALT = 'S0m3S4lT' ;

private $userRepository;

public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}

public function execute($aUsername, $aPassword)
{
if (!$this->userRepository->has($aUsername)) {
throw new InvalidArgumentException(
sprintf('The user "%s" does not exist.', $aUsername)
);
}

$aUser = $this->userRepository->byUsername($aUsername);

if ($this->isPasswordInvalidFor($aUser, $aPassword)) {
throw new BadCredentialsException($aUser, $aPassword);
}

return $aUser;
}

private function salt()
{
return md5(self::SALT);
}

private function isPasswordInvalidFor(
User $aUser, $anUnencryptedPassword
) {
$encryptedPassword = md5(
$anUnencryptedPassword . '_' .$this->salt()
);

return $aUser->hash() !== $encryptedPassword;
}
}

Opting for this choice allows us to have multiple implementations of the Domain Service interface at the Infrastructure layer. In other words, we end up with several Infrastructure Domain Services. Each Infrastructure service will be responsible for handling a different hash mechanism. Depending on the implementation, the use can easily be managed through a Dependency Injection container — for example, through Symfony's Dependency Injection component:

<?xml version="1.0"?>
<container
xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>

<service id="sign_in" alias="sign_in.default" />

<service id="sign_in.default"
class="DddAuthInfrastructureAuthentication
DefaultHashingSignUp">
<argument type="service" id="user_repository"/>
</service>

<service id="sign_in.md5"
class="DddAuthInfrastructureAuthentication
Md5HashingSignUp">
<argument type="service" id="user_repository"/>
</service>

</services>
</container>

If, in the future, we wish to handle a new type of hashing, we can simply start by implementing the Domain Service interface. Then it's a matter of declaring the service in the Dependency Injection container and replacing the service alias dependency with the newly created one.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.54.7