diff --git a/phpstan.neon b/phpstan.neon index 6d55e95..c3ece0f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,7 +2,7 @@ includes: - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/rules.neon - lib/phpstan/phpstan.neon - - vendor/phpat/phpat/extension.neon + - vendor/phpat/phpat/extension.neon parameters: bootstrapFiles: diff --git a/src/ArtyCodingStandard/PHPAt/CleanArchitectureTest.php b/src/ArtyCodingStandard/PHPAt/CleanArchitectureTest.php new file mode 100644 index 0000000..a5a1f1e --- /dev/null +++ b/src/ArtyCodingStandard/PHPAt/CleanArchitectureTest.php @@ -0,0 +1,78 @@ +classes(Selector::inNamespace('App\Domain')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('App\Application')) + ->because('Domain is the core layer and must remain independent of outer layers'); + } + + public function testDomainDoesNotDependOnInfrastructure(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('App\Domain')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('App\Infrastructure')) + ->because('Domain must not know about infrastructure concerns (DB, HTTP, etc.)'); + } + + public function testDomainDoesNotDependOnSymfony(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('App\Domain')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('Symfony')) + ->because('Domain must be framework-agnostic'); + } + + public function testDomainDoesNotDependOnDoctrine(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('App\Domain')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('Doctrine')) + ->because('Domain must not depend on persistence concerns'); + } + + public function testApplicationDoesNotDependOnInfrastructure(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('App\Application')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('App\Infrastructure')) + ->because('Application layer must depend on Domain abstractions, not Infrastructure implementations'); + } + + public function testApplicationDoesNotDependOnSymfony(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('App\Application')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('Symfony')) + ->because('Application use-cases must be framework-agnostic'); + } + + public function testApplicationDoesNotDependOnDoctrine(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('App\Application')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('Doctrine')) + ->because( + 'Application layer must not depend on persistence concerns' + . ' — use Domain repository interfaces instead' + ); + } +}