24
PHPArkitect: Put your architectural rules under test!
In every project there is at least an architectural rule usually like: every class inside Controller directory should be called with suffix Controller.
I have seen a lot of projects in my career with many different types of rules.
Other examples could be when a team tries to apply the hexagonal architecture and inside the domain, you shouldn’t call external libraries or framework classes (there are exceptions but this is out of scope).
Usually, a team shares those rules during retrospective or dedicated moments to improve the software and to establish rules to follow.
But after some months usually, those rules could be violated by new members of the team or someone that doesn’t remember them.
So the next step is to write down into a README file or in a file all the rules but again they can be violated.
Another good solution is to write ADR: Architectural Decision Records that explain the choice and the reason, but still, they can guarantee that will not be violated.
You need a tool to validate your architectural rules.
PHPArkitect can do it for you.
PHPArkitect is a tool that helps you to keep your PHP codebase coherent and solid, by permitting you to add some architectural constraint check to your workflow.
It was born in 2020 to satisfy the need to respect architectural rules in various projects.
In Java there is a similar tool from which we took inspiration: archunit.
Now PHPArkitect is public and you can find it here
As I explained before, PHPArkitect was born to help teams to define architectural rules in a project.
With this tool, you can add a new step with it into your continuous integration and you have all your architectural rules in a file, so new developers can understand easily the constraints of the project.
To install PHPArkitect you can use composer launching this command into your CLI:
composer require — dev phparkitect/phparkitect
You can also use a .phar file because you can have some conflicts with one or more of Phparkitect’s dependencies.
The Phar can be downloaded from GitHub:
wget [https://github.com/phparkitect/arkitect/releases/latest/download/phparkitect.phar](https://github.com/phparkitect/arkitect/releases/latest/download/phparkitect.phar)
chmod +x phparkitect.phar
./phparkitect.phar check
To use PHPArkitect it’s necessary to create a file inside your root called phparkitect.php with your rules written inside it like this:
<?php
declare(strict_types=1);
use Arkitect\ClassSet;
use Arkitect\CLI\Config;
use Arkitect\Expression\ForClasses\HaveNameMatching;
use Arkitect\Expression\ForClasses\ResideInOneOfTheseNamespaces;
use Arkitect\Rules\Rule;
return static function (Config $config): void {
$classSet = ClassSet::fromDir(__DIR__ . ‘/src’);
$r1 = Rule::allClasses()
->that(new ResideInOneOfTheseNamespaces(‘App\Order\Infrastructure\Controller’))
->should(new HaveNameMatching(‘*Controller’))
->because(“we want uniform naming”);
$config->add($classSet, $r1);
};
And now you can launch from your CLI the command:
./vendor/bin/phparkitect check
If you want to use a different name for your file you can specify it when you launch the command like this:
./vendor/bin/phparkitect check — config=/project/yourConfigFile.php
With this tool, you can write your own rules for your project.
At the moment you can check if a class:
- depends on a namespace
- extends another class
- not extend another class
- have a name matching a pattern
- not have a name matching a pattern
- implements an interface
- not implement an interface
- depends on a namespace
- don’t have dependency outside a namespace
- reside in a namespace
- not reside in a namespace
Let’s make some examples:
Assume that if you want those classes inside a namespace that doesn’t depend on classes outside that namespace you can write this rule:
$rule = Rule::allClasses()
->that(new ResideInOneOfTheseNamespaces(‘App\Catalog\Domain’))
->should(new NotHaveDependencyOutsideNamespace(‘App\Catalog\Domain’))
->because(‘domain should not have external dependencies’);
If you want to be sure that classes inside a namespace are following certain naming rules you write a rule like this:
$rule = Rule::allClasses()
->that(new ResideInOneOfTheseNamespaces(‘App\Order\Infrastructure\Controller’))
->should(new HaveNameMatching(‘*Controller’))
->because(“we want uniform naming”);
If you want to be sure that all classes that extend a class are following certain naming rules you write a rule like this:
$rule = Rule::allClasses()
->that(new Extend(AbstractType::class))
->should(new HaveNameMatching(‘*Type’))
->because(‘we want uniform form type naming’);
You can also exclude some classes from the rules for example:
$rule = Rule::allClasses()
->exclude([‘App\Order\Application\Service\Foo’])
->that(new ResideInOneOfTheseNamespaces(‘App\Order\Application\Service’))
->should(new HaveNameMatching(‘*Service’))
->because(“we want uniform naming”);
PHPArkitect it’s young at the moment and a lot of things could be developed.
If you are interested you can:
- try the tool
- improve the documentation
- try to write your own rule
- open pull requests to fix bugs or to discuss new features with a draft
- open issues to ask something or to reveal a bug
You can help with a little effort to improve this project and help other developers.
24