4

Imagine a "clean architecture" in which you have two use cases. One of them is CreateCustomerHandler, and the other SignUpCustomerByGoogleAuthHandler. So, the SignUpCustomerByGoogleAuthHandler has to reuse the CreateCustomerHandler to eventually create a Customer.

Is it legit to inject CreateCustomerHandler to SignUpCustomerByGoogleAuthHandler as dependency? Are there any pitfalls?

I read somewhere that it's not recommended to reuse your use cases, but what to do in that simple case? Extract the whole CreateCustomerHandler as Application Service and inject it both to the CreateCustomerHandler and SignUpCustomerByGoogleAuthHandler use cases?

CreateCustomerHandler

<?php

declare(strict_types=1);

namespace App\Identity\Application\Customer\UseCase\CreateCustomer;

use App\Identity\Application\Customer\CustomerEntityManager;
use App\Identity\Application\Security\PasswordEncoder;
use App\Identity\Domain\Customer\Customer;
use App\Identity\Domain\Customer\Name;
use App\Identity\Domain\Customer\Username;
use App\Identity\Domain\User\Email;
use App\Identity\Domain\User\Password;

final class CreateCustomerHandler
{
    private CustomerEntityManager $customerEntityManager;

    private PasswordEncoder $passwordEncoder;

    public function __construct(CustomerEntityManager $customerEntityManagerByActiveTenant,
                                PasswordEncoder $passwordEncoder)
    {
        $this->customerEntityManager = $customerEntityManagerByActiveTenant;
        $this->passwordEncoder = $passwordEncoder;
    }

    public function handle(CreateCustomerCommand $command): Customer
    {
        $customerId = $this->customerEntityManager->nextId();
        $email = new Email($command->email());
        $username = new Username($command->username());
        $name = new Name($command->firstname(), $command->lastname());
        $password = $this->passwordEncoder->encodePassword($command->password());
        $password = new Password($password);

        $customer = new Customer(
            $customerId,
            $email,
            $password,
            $username,
            $name,
        );

        $this->customerEntityManager->create($customer);

        return $customer;
    }
}

SignInCustomerByGoogleAuthHandler

<?php

declare(strict_types=1);

namespace App\Identity\Application\Customer\UseCase\SignInCustomerByGoogleAuth;

use App\Identity\Application\Customer\CustomerEntityManager;
use App\Identity\Application\Customer\Query\CustomerByGoogleAuthQuery;
use App\Identity\Application\Customer\UseCase\CreateCustomer\CreateCustomerCommand;
use App\Identity\Application\Customer\UseCase\CreateCustomer\CreateCustomerHandler;
use App\Identity\Application\Security\Security;
use App\Identity\Application\User\UseCase\LinkGoogleAuth\GoogleAuth;
use App\Identity\Domain\Customer\Customer;

final class SignInCustomerByGoogleAuthHandler
{
    private CustomerByGoogleAuthQuery $customerByGoogleAuthQuery;

    private GoogleAuth $googleAuth;

    private CreateCustomerHandler $createCustomerHandler;

    private CustomerEntityManager $customerEntityManager;

    private Security $security;

    public function __construct(CustomerByGoogleAuthQuery $customerByGoogleAuthQuery,
                                CreateCustomerHandler $createCustomerHandler,
                                CustomerEntityManager $customerEntityManager,
                                Security $security,
                                GoogleAuth $googleAuth)
    {
        $this->customerByGoogleAuthQuery = $customerByGoogleAuthQuery;
        $this->googleAuth = $googleAuth;
        $this->createCustomerHandler = $createCustomerHandler;
        $this->customerEntityManager = $customerEntityManager;
        $this->security = $security;
    }

    public function handle(SignInCustomerByGoogleAuthCommand $command): Customer
    {
        $code = $command->code();

        $oauthUser = $this->googleAuth->userByOneTimeCode($code);
        $customer = $this->customerByGoogleAuthQuery->queryByGoogleUser($oauthUser);
        $username = $this->customerEntityManager->nextId()->value();
        $password = $this->security->randomPassword();

        if (!$customer) {
            $command = new CreateCustomerCommand(
                $oauthUser->email(),
                $username,
                $password->hash(),
                $oauthUser->firstname(),
                $oauthUser->lastname(),
            );
            $customer = $this->createCustomerHandler->handle($command);
        }

        // TODO $customer->allowAuthVendor();

        return $customer;
    }
}
13
  • 1
    please don't cross-post: stackoverflow.com/questions/67671691/… "Cross-posting is frowned upon as it leads to fragmented answers splattered all over the network..."
    – gnat
    May 24, 2021 at 11:59
  • 1
    @gnat sorry, wasn't really sure to which community this question is more related. Let's stick to the SO version or this one?
    – Clark
    May 24, 2021 at 12:01
  • 2
    Please be clear with your terminology. A Use Case is not code, it is a description of functionality the application must have to satisfy a user's need. A handler is code, but depending on whatever your framework is, the nuance of how "Handler" is defined depends on that context. Code questions on Stack Overflow, Design/Process questions here. May 24, 2021 at 12:53
  • 2
    @BerinLoritsch This is yet another unfortunate wording used by R.C.Martin in his "clean architecture", which creates a lot of confusion. What he calls "use-cases" correspond more to some transactional logic than to anything related to a real use-case...
    – Christophe
    May 24, 2021 at 14:22
  • 4
    Does this answer your question? Clean architecture - How do I deal with use case reuse?
    – Christophe
    May 24, 2021 at 16:25

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.