sensiolabs-de/deptrac-shim
Deptrac shim provides a standalone binary to run Deptrac architecture checks without a full Composer install, making it easy to enforce layer dependencies in CI or quick local checks. Lightweight wrapper for PHP projects.
Deptrac is a static-analysis tool that enforces architecture rules — which layers of your codebase are allowed to talk to which. It is not a runtime Laravel package, so it belongs in require-dev and in CI, not in your app's service container.
composer require --dev qossmic/deptrac
The 1.0.2 release is a patch on the 1.x line: bug fixes only, no config schema change, no new required PHP version beyond the 8.0+ floor of 1.x. If you are already on 1.0.x, composer update qossmic/deptrac is a drop-in — no deptrac.yaml edits required. If you are still on 0.x, treat it as a fresh install: the config file moved from depfile.yml to deptrac.yaml and the schema (parameters:, layers:, ruleset:) changed.
Where to look first:
deptrac.yaml at the project root — the whole tool is this one file.vendor/bin/deptrac analyse — the only command you need day-to-day. --help is the real documentation.Your first use case should be the coarsest, most valuable boundary you actually care about — typically:
App\Http must not depend on App\Models directly (go through App\Actions/App\Services), orApp\Domain must not depend on App\Infrastructure, orApp\ may depend on a specific legacy namespace, ever.Start with two or three layers. A 15-layer ruleset written on day one will produce 400 violations and get deleted by Friday.
Layers by directory (the Laravel workhorse). The directory collector value is a regex matched against the file path, not a glob:
layers:
- name: Http
collectors:
- type: directory
value: app/Http/.*
- name: Domain
collectors:
- type: directory
value: app/Domain/.*
- name: Infrastructure
collectors:
- type: directory
value: app/Infrastructure/.*
Vendor/framework boundaries. Use the composer collector to pin what your Domain layer is allowed to import — e.g. forbid laravel/framework and guzzlehttp/guzzle inside App\Domain so your core stays framework-agnostic:
- name: Domain
collectors:
- type: directory
value: app/Domain/.*
- type: composer
value: illuminate/.* # or exclude via ruleset instead
Ruleset. ruleset lists, per layer, the layers it may depend on. An empty list means "depends on nothing but itself":
ruleset:
Http: [Domain, Application]
Application: [Domain]
Infrastructure:[Domain]
Domain: []
Layer names are strings, not paths — keep them short and stable, because they become your architecture vocabulary in PR reviews.
Legacy adoption via baseline. Do not fix 300 violations before turning the tool on. Generate a baseline and import it:
vendor/bin/deptrac analyse --formatter=baseline --output=deptrac-baseline.yaml
imports:
- deptrac-baseline.yaml
New violations then fail CI while the old debt is frozen. Regenerate the baseline deliberately and only when you have actually paid down entries — see gotchas.
CI integration. In GitHub Actions use the dedicated formatter so violations annotate the diff:
- run: vendor/bin/deptrac analyse --formatter=github-actions --no-progress
A Composer script keeps it discoverable alongside PHPStan/Pint:
"scripts": { "analyse": ["@php vendor/bin/deptrac analyse", "phpstan analyse"] }
Visualising the rules you actually wrote. --formatter=graphviz --output=deptrac.dot then render to PNG. This is the fastest way to convince a sceptical team, and the fastest way to spot a ruleset that permits everything to everything.
Caching for large repos. Add --cache-file=.deptrac.cache (and gitignore it) on repos where analysis takes more than a few seconds. Combined with --no-progress, this makes a pre-commit hook viable.
Pair it with PHPStan, don't replace it. Deptrac answers "which namespace may reference which"; PHPStan answers "is this call correct". Running both in the same CI job is the normal split.
directory collector values are regexes. app/Domain/.* is correct; app/Domain/* is not. Dot-escape where it matters (app/Support/.*). A silently non-matching collector yields an empty layer and a ruleset that passes for the wrong reason — always sanity-check with --report-uncovered.--fail-on-uncovered in CI once your layers cover the app reasonably well.+Layer inheritance is sharper than it looks. Prefixing a layer with + in a ruleset pulls in that layer's allowed dependencies transitively. It is convenient and it is the single easiest way to accidentally grant Http → Infrastructure. Prefer explicit lists; reach for + only for genuinely shared base layers.deptrac.yaml, especially a collector regex, delete the cache file. Symptoms of staleness are "I changed the rule and nothing happened."ruleset errors at run time, not at composer install — so a broken config lands on whoever pushes next. Run deptrac analyse locally once after any config edit.skip_violations is a scalpel with no handle. Keyed by class → list of classes, it suppresses a specific edge. Prefer the baseline for bulk legacy debt and skip_violations for the handful of genuinely intentional exceptions, each with a comment or a linked issue in the PR.A → B → C), and the class you actually need to change is usually the last hop, not the first.composer collector needs a resolved vendor tree. It reads Composer's installed-package metadata; on a fresh clone, run composer install before deptrac analyse, or CI will report a confusing empty layer.imports. Rather than one monster file, split deptrac-layers.yaml (shared layer definitions, possibly a published baseline) from per-team rulesets and imports them. This is how monorepos keep the config reviewable.^1.0 in composer.json rather than a bare version so patch fixes like this arrive automatically, and let the lockfile be the thing that decides when you move.How can I help you explore Laravel packages today?