boundwize / structarmed
Configurable PHP architecture guards — define your layers and rules, then keep them enforced
0.0.1
2026-05-08 09:40 UTC
Requires
- php: ^8.2
- nikic/php-parser: ^5.7
Requires (Dev)
- laminas/laminas-coding-standard: ^3.1
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
- rector/rector: dev-main
README
Configurable PHP architecture guards — define your layers and rules, then keep them enforced.
Installation
composer require --dev boundwize/structarmed
Quick start
vendor/bin/structarmed init
Generates a structarmed.php in your project root. Edit it to match your structure, then run:
vendor/bin/structarmed analyse
If violations are found, the output reports each one:
If everything passes, you get a clean summary:
Configuration
Default
// structarmed.php use Boundwize\StructArmed\Architecture; use Boundwize\StructArmed\Preset\Preset; return Architecture::define() ->withPreset(Preset::PSR4());
Multiple presets
->withPresets(Preset::PSR4(), Preset::DDD(), Preset::MVC())
Custom layers and rules
use Boundwize\StructArmed\Architecture; use Boundwize\StructArmed\Rule\Rules\Layer\MayNotDependOnRule; use Boundwize\StructArmed\Rule\Rules\Method\MustHaveReturnTypeRule; return Architecture::define() ->layer('Domain', 'src/Domain/') ->layer('Application', 'src/Application/') ->layer('Infrastructure', 'src/Infrastructure/') ->skip([ 'tests/Fixtures/', 'var/cache/*', DddPreset::ENTITY_MUST_BE_FINAL => ['src/Legacy/'], ]) ->rule( 'domain.must_not_depend_on_infrastructure', new MayNotDependOnRule(from: 'Domain', to: 'Infrastructure', toPath: 'Infrastructure') ) ->rule( 'domain.public_methods_must_have_return_types', new MustHaveReturnTypeRule(layer: 'Domain') );
Override preset rules
Use rule key constants — never raw strings:
use Boundwize\StructArmed\Preset\Presets\DddPreset; return Architecture::define() ->layer('Domain', 'src/Domain/') ->layer('Application', 'src/Application/') ->layer('Infrastructure', 'src/Infrastructure/') ->withPreset(Preset::DDD()) // Remove a rule entirely ->withoutRule(DddPreset::DOMAIN_NO_BASE_EXCEPTION) // Replace a rule with a different configuration ->replaceRule( DddPreset::ENTITY_MUST_BE_FINAL, new MustBeFinalRule(layer: 'Domain', classNamePattern: '/Entity$|Aggregate$/') ) // Add your own custom rule ->rule( 'our.handlers.must_be_in_application', new NamingConventionRule( classNamePattern: '/Handler$/', mustBeInLayer: 'Application' ) );
Preset constructor parameters
->withPreset(Preset::DDD( maxComplexity: 3, // default: 5 maxMethodLength: 15, // default: 20 enforceFinalEntities: false, // default: true )) ->withPreset(Preset::MVC( controllerMaxComplexity: 3, // default: 5 controllerMaxDependencies: 4, // default: 5 viewMaxComplexity: 2, // default: 3 )) ->withPreset(Preset::PSR4( sourcePaths: ['src/', 'tests/'], // default: read composer.json PSR-4 paths ))
Available presets
| Preset | Rules |
|---|---|
Preset::PSR4() |
Verifies configured source paths exist in composer.json autoload or autoload-dev PSR-4 mappings |
Preset::DDD() |
Layer isolation, entity/VO/repository/event/service conventions |
Preset::MVC() |
Layer isolation, thin controllers, model/view/service rules |
PHPUnit extension
Run architecture checks as part of your test suite:
<!-- phpunit.xml --> <extensions> <bootstrap class="Boundwize\StructArmed\PHPUnit\StructArmedExtension"/> </extensions>
Violations cause the test run to fail before any tests execute.
CLI
# Analyse with default config discovery vendor/bin/structarmed analyse vendor/bin/structarmed analyze # Analyse only specific paths vendor/bin/structarmed analyse src vendor/bin/structarmed analyze src tests # Custom config path vendor/bin/structarmed analyse --config=path/to/structarmed.php vendor/bin/structarmed analyze --config=path/to/structarmed.php # JSON output (for CI tools) vendor/bin/structarmed analyse --report=json vendor/bin/structarmed analyze --report=json # Generate initial config vendor/bin/structarmed init
Layer resolution
Layers are resolved by file path — no attributes needed on classes:
src/Domain/ → 'Domain'
src/Application/ → 'Application'
src/Infrastructure/ → 'Infrastructure'
Rule key constants
Every preset rule has a public constant. Use constants, never raw strings:
// ✅ correct — caught by IDE and static analysis DddPreset::ENTITY_MUST_BE_FINAL // ❌ wrong — typo silently does nothing 'ddd.entity.must_be_fnal'
Requirements
- PHP 8.2 or higher
nikic/php-parser^5.0