From 2afc742d6044130718e6af9206b1ed5379ec8066 Mon Sep 17 00:00:00 2001 From: Kevin Kaniaburka Date: Sat, 7 Sep 2024 15:42:47 +0200 Subject: [PATCH] Add architecture test to prevent domain layer being coupled with 3rd party libraries --- .../Provider/AuthorizedUserProviderSpec.php | 2 +- src/Account/Domain/Factory/UserFactory.php | 2 +- .../Hasher/UserPasswordHasherInterface.php | 12 ++++++++ src/Account/Domain/Model/User.php | 14 --------- src/Account/Domain/Model/UserInterface.php | 6 ++-- .../Repository/UserRepositoryInterface.php | 3 +- .../Symfony/Service/provider.php | 2 +- .../Doctrine/Orm/UserRepository.php | 2 +- .../Security}/AuthorizedUserProvider.php | 3 +- .../Infrastructure/Symfony/Security/User.php | 24 +++++++++++++++ .../Symfony/Security/UserInterface.php | 13 +++++++++ .../Security/UserRepositoryInterface.php | 12 ++++++++ .../Symfony/Package/security.yaml | 2 +- .../Calculator/OperationValueCalculator.php | 7 +++-- .../ExchangeRateLogNotFoundException.php | 10 +++++++ .../Exception/EmptyAdjustmentsException.php | 10 +++++++ .../Domain/Factory/TransactionFactory.php | 6 ++-- .../Domain/Factory/UserFactoryTest.php | 2 +- tests/Architecture/LayersSeparationTest.php | 29 ++++++++++++++----- 19 files changed, 123 insertions(+), 38 deletions(-) create mode 100644 src/Account/Domain/Hasher/UserPasswordHasherInterface.php rename src/Account/{Domain/Provider => Infrastructure/Symfony/Security}/AuthorizedUserProvider.php (89%) create mode 100644 src/Account/Infrastructure/Symfony/Security/User.php create mode 100644 src/Account/Infrastructure/Symfony/Security/UserInterface.php create mode 100644 src/Account/Infrastructure/Symfony/Security/UserRepositoryInterface.php create mode 100644 src/Report/Domain/Exception/ExchangeRateLogNotFoundException.php create mode 100644 src/Trade/Domain/Exception/EmptyAdjustmentsException.php diff --git a/spec/Account/Domain/Provider/AuthorizedUserProviderSpec.php b/spec/Account/Domain/Provider/AuthorizedUserProviderSpec.php index aa367c4..382df7e 100644 --- a/spec/Account/Domain/Provider/AuthorizedUserProviderSpec.php +++ b/spec/Account/Domain/Provider/AuthorizedUserProviderSpec.php @@ -4,7 +4,7 @@ use Panda\Account\Domain\Exception\AuthorizedUserNotFoundException; use Panda\Account\Domain\Model\UserInterface; -use Panda\Account\Domain\Provider\AuthorizedUserProvider; +use Panda\Account\Infrastructure\Symfony\Security\AuthorizedUserProvider; use Panda\AccountOHS\Domain\Provider\AuthorizedUserProviderInterface; use PhpSpec\ObjectBehavior; use Symfony\Bundle\SecurityBundle\Security; diff --git a/src/Account/Domain/Factory/UserFactory.php b/src/Account/Domain/Factory/UserFactory.php index d20b5df..2a1bba7 100644 --- a/src/Account/Domain/Factory/UserFactory.php +++ b/src/Account/Domain/Factory/UserFactory.php @@ -4,9 +4,9 @@ namespace Panda\Account\Domain\Factory; +use Panda\Account\Domain\Hasher\UserPasswordHasherInterface; use Panda\Account\Domain\Model\User; use Panda\Account\Domain\Model\UserInterface; -use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; final readonly class UserFactory implements UserFactoryInterface { diff --git a/src/Account/Domain/Hasher/UserPasswordHasherInterface.php b/src/Account/Domain/Hasher/UserPasswordHasherInterface.php new file mode 100644 index 0000000..9a598ba --- /dev/null +++ b/src/Account/Domain/Hasher/UserPasswordHasherInterface.php @@ -0,0 +1,12 @@ +email = $email; } - public function getUserIdentifier(): string - { - return $this->email; - } - - public function getRoles(): array - { - return ['ROLE_USER']; - } - public function getPassword(): ?string { return $this->password; @@ -54,10 +44,6 @@ public function setPassword(string $password): void $this->password = $password; } - public function eraseCredentials(): void - { - } - public function compare(OwnerInterface $owner): bool { return $this->getId() === $owner->getId(); diff --git a/src/Account/Domain/Model/UserInterface.php b/src/Account/Domain/Model/UserInterface.php index a32e551..6b51638 100644 --- a/src/Account/Domain/Model/UserInterface.php +++ b/src/Account/Domain/Model/UserInterface.php @@ -6,14 +6,14 @@ use Panda\AccountOHS\Domain\Model\Owner\OwnerInterface; use Panda\Core\Domain\Model\TimestampableInterface; -use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; -use Symfony\Component\Security\Core\User\UserInterface as SymfonyUserInterface; -interface UserInterface extends TimestampableInterface, OwnerInterface, SymfonyUserInterface, PasswordAuthenticatedUserInterface +interface UserInterface extends TimestampableInterface, OwnerInterface { public function getEmail(): string; public function setEmail(string $email): void; + public function getPassword(): ?string; + public function setPassword(string $password): void; } diff --git a/src/Account/Domain/Repository/UserRepositoryInterface.php b/src/Account/Domain/Repository/UserRepositoryInterface.php index 48aad87..ed42bf6 100644 --- a/src/Account/Domain/Repository/UserRepositoryInterface.php +++ b/src/Account/Domain/Repository/UserRepositoryInterface.php @@ -5,10 +5,9 @@ namespace Panda\Account\Domain\Repository; use Panda\Account\Domain\Model\UserInterface; -use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Uid\Uuid; -interface UserRepositoryInterface extends PasswordUpgraderInterface +interface UserRepositoryInterface { public function save(UserInterface $user): void; diff --git a/src/Account/Infrastructure/Configuration/Symfony/Service/provider.php b/src/Account/Infrastructure/Configuration/Symfony/Service/provider.php index 783471a..69d2b8c 100644 --- a/src/Account/Infrastructure/Configuration/Symfony/Service/provider.php +++ b/src/Account/Infrastructure/Configuration/Symfony/Service/provider.php @@ -2,7 +2,7 @@ declare(strict_types=1); -use Panda\Account\Domain\Provider\AuthorizedUserProvider; +use Panda\Account\Infrastructure\Symfony\Security\AuthorizedUserProvider; use Panda\AccountOHS\Domain\Provider\AuthorizedUserProviderInterface; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use function Symfony\Component\DependencyInjection\Loader\Configurator\service; diff --git a/src/Account/Infrastructure/Doctrine/Orm/UserRepository.php b/src/Account/Infrastructure/Doctrine/Orm/UserRepository.php index 6ead842..f163332 100644 --- a/src/Account/Infrastructure/Doctrine/Orm/UserRepository.php +++ b/src/Account/Infrastructure/Doctrine/Orm/UserRepository.php @@ -5,7 +5,7 @@ use Doctrine\ORM\EntityManagerInterface; use Panda\Account\Domain\Model\User; use Panda\Account\Domain\Model\UserInterface; -use Panda\Account\Domain\Repository\UserRepositoryInterface; +use Panda\Account\Infrastructure\Symfony\Security\UserRepositoryInterface; use Panda\Core\Infrastructure\Doctrine\Orm\DoctrineRepository; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; diff --git a/src/Account/Domain/Provider/AuthorizedUserProvider.php b/src/Account/Infrastructure/Symfony/Security/AuthorizedUserProvider.php similarity index 89% rename from src/Account/Domain/Provider/AuthorizedUserProvider.php rename to src/Account/Infrastructure/Symfony/Security/AuthorizedUserProvider.php index 584dfe3..a6bc18b 100644 --- a/src/Account/Domain/Provider/AuthorizedUserProvider.php +++ b/src/Account/Infrastructure/Symfony/Security/AuthorizedUserProvider.php @@ -2,10 +2,9 @@ declare(strict_types=1); -namespace Panda\Account\Domain\Provider; +namespace Panda\Account\Infrastructure\Symfony\Security; use Panda\Account\Domain\Exception\AuthorizedUserNotFoundException; -use Panda\Account\Domain\Model\UserInterface; use Panda\AccountOHS\Domain\Model\Owner\OwnerInterface; use Panda\AccountOHS\Domain\Provider\AuthorizedUserProviderInterface; use Symfony\Bundle\SecurityBundle\Security; diff --git a/src/Account/Infrastructure/Symfony/Security/User.php b/src/Account/Infrastructure/Symfony/Security/User.php new file mode 100644 index 0000000..70eb633 --- /dev/null +++ b/src/Account/Infrastructure/Symfony/Security/User.php @@ -0,0 +1,24 @@ +getEmail(); + } + + public function getRoles(): array + { + return ['ROLE_USER']; + } + + public function eraseCredentials(): void + { + } +} diff --git a/src/Account/Infrastructure/Symfony/Security/UserInterface.php b/src/Account/Infrastructure/Symfony/Security/UserInterface.php new file mode 100644 index 0000000..61c8dd2 --- /dev/null +++ b/src/Account/Infrastructure/Symfony/Security/UserInterface.php @@ -0,0 +1,13 @@ +format('Y-m-d H:i:s'))); + } return $operation->getQuantity() * $exchangeRate->getRate(); } diff --git a/src/Report/Domain/Exception/ExchangeRateLogNotFoundException.php b/src/Report/Domain/Exception/ExchangeRateLogNotFoundException.php new file mode 100644 index 0000000..2c7ff7c --- /dev/null +++ b/src/Report/Domain/Exception/ExchangeRateLogNotFoundException.php @@ -0,0 +1,10 @@ +classes(...$this->applicationLayerSelectors, ...$this->infrastructureLayerSelectors); } - public function test_domain_does_not_depend_on_doctrine(): Rule + public function test_domain_does_not_depend_on_vendor(): Rule { $this->findAllLayers(); return PHPat::rule() ->classes(...$this->domainLayerSelectors) - ->shouldNotDependOn() - ->classes(Selector::namespace('Doctrine')) + ->canOnlyDependOn() + ->classes( + Selector::namespace('Panda'), + + // Allowed 3rd party classes + Selector::classname(Uuid::class), + Selector::classname(Collection::class), + Selector::classname(ArrayCollection::class), - // FIXME: requires too much effort to get rid of these dependencies for now - ->excluding( - Selector::classname('Doctrine\Common\Collections\ArrayCollection'), - Selector::classname('Doctrine\Common\Collections\Collection'), + // PHP root namespace + Selector::classname(\BackedEnum::class), + Selector::classname(\Countable::class), + Selector::classname(\DateTimeImmutable::class), + Selector::classname(\DateTimeInterface::class), + Selector::classname(\Exception::class), + Selector::classname(\InvalidArgumentException::class), + Selector::classname(\Iterator::class), + Selector::classname(\IteratorAggregate::class), + Selector::classname(\Throwable::class), ); }