Glossary
Deptrac
Directories alone do not enforce architecture. Deptrac turns agreed boundaries between layers into a check that can fail as early as a pull request.
Short definition
Checking who is allowed to depend on whom.
Deptrac analyses references between classes, interfaces, traits, and other code elements. It first assigns them to layers such as Domain, Application, Infrastructure, and UI. Rules then specify which layers may use which others; an invalid dependency direction is reported as a violation.
It is not a tool that decides whether an application is well designed. It does, however, make it possible to continuously enforce specific team decisions: for example, that a domain rule must not import an ORM, a controller must not work directly with a database adapter, and infrastructure must remain at the edge of the system.
Use cases
Where automated boundaries make sense
The check is worthwhile where an application evolves over a longer period and an incorrect dependency direction would gradually make changes and testing harder.
- a modular monolith with multiple domain modules
- Clean or hexagonal architecture separating logic from adapters
- an internal system where HTTP, CLI, and a queue share the same application use cases
- an e-commerce site with separate catalogue, orders, inventory, and integrations
- restricting direct use of the framework or selected Composer packages outside infrastructure
- a CI check that a new pull request does not bypass agreed boundaries
Practical example
Four layers of an order import
In the orders module, Domain contains rules and value objects. Application contains the ImportMarketplaceOrder use case and its port for storing an order. Infrastructure implements this port through a database or HTTP client. UI accepts an HTTP request or CLI command and passes it to the application layer.
If a controller began calling a Doctrine repository directly, or Domain imported a Laravel or Symfony class, Deptrac could reject the change. This does not confirm that the import works; it only verifies that the chosen boundary was not bypassed.
deptrac:
paths: [./src]
layers:
- name: Domain
collectors: [{ type: classLike, value: '^App\\Domain\\.*' }]
- name: Application
collectors: [{ type: classLike, value: '^App\\Application\\.*' }]
- name: Infrastructure
collectors: [{ type: classLike, value: '^App\\Infrastructure\\.*' }]
- name: UI
collectors: [{ type: classLike, value: '^App\\UI\\.*' }]
ruleset:
Domain: ~
Application: [Domain]
Infrastructure: [Application, Domain]
UI: [Application]
How it works
From code to an architectural rule
Configuration should describe a real application boundary, not merely copy directory names mechanically.
- Paths Deptrac loads the designated directories and builds a map of dependencies between PHP code elements.
- Collectors A collector determines which elements belong to a layer; it can select them by fully qualified class name, directory, attribute, interface, or Composer package, for example.
- Layers Named groups express a view of the application. Depending on the configuration, one element may belong to multiple layers, which is why output and warnings must be reviewed.
- Ruleset Rules allow specific dependency directions. A dependency between layers is prohibited by default until the ruleset permits it.
- Report and CI The analysis identifies the file, line, and prohibited direction. CI can then block new violations before merging.
Key concepts
Layers, collectors, and rules
Deptrac does not check one universal architecture. It checks the model described by the application itself.
Layers
Layers are groups of code elements, such as Domain or UI. They are not automatically the same as directories, although a directory is often a practical starting point.
Collectors
Collectors assign elements to a layer. The common classLike collector matches a fully qualified name, directory follows a file path, and composer can delimit use of a package.
Ruleset
The ruleset expresses permitted dependencies. Application may know Domain; Infrastructure may implement a port defined in Application; UI should call Application, not a database adapter.
Violations and uncovered dependencies
A violation is a prohibited reference between layers. An uncovered dependency means that an analysed element references a class not assigned to any layer; it is not itself a ruleset violation.
Benefits and limitations
Protecting boundaries, not replacing design
Benefits
- architectural intent is readable as versioned configuration
- a violation appears with the specific change, not only during a difficult refactoring
- helps preserve the independence of modules and domain logic from the framework
- the same check runs locally and in CI
Limitations and common mistakes
- a green report does not say whether the layers make sense for the business
- overly general regular expressions and layers can create a false sense of control
- complex configuration and many exceptions can cost more than the protected part of the application
- a Domain directory without real rules is not automatically a domain model
Scope of use
Rules should grow with the project’s real complexity.
A small script or simple administration form does not need four layers merely to pass Deptrac. For a long-lived module with external APIs, a queue, a database, and multiple entry points, however, it is worth ensuring that technical details do not leak into application and domain logic.
For an older application, known violations can be temporarily excluded with skip_violations or a generated baseline. Exceptions should have a reason and an owner; repeatedly expanding them without removing any means that CI no longer protects new changes sufficiently.
What to consider
Maintain rules as part of the application
A useful report is specific, understandable to the team, and based on the real direction of dependencies.
- start with a few meaningful boundaries instead of a complete map of every class
- name layers by responsibility, not by a fashionable pattern
- report uncovered dependencies and decide whether they need to be covered
- run Deptrac in CI alongside tests and type analysis
- regularly reduce exceptions and the baseline and review them during code review
Common questions
What Deptrac verifies
Does Deptrac replace PHPStan or PHPUnit?
No. PHPStan finds type and contract errors, while PHPUnit verifies the behaviour of a running application. Deptrac checks only the permitted direction of dependencies between layers.
Is Deptrac necessary for every PHP application?
No. It provides the greatest benefit in long-lived modules with clearly separated responsibilities. In a small, straightforward project, its configuration may be unnecessary overhead.
Is placing classes in directories enough?
No. A directory is an organisational aid, but without a ruleset nothing prevents a controller from importing an infrastructure repository or the domain from using a framework class.
Are a baseline and skip_violations a mistake?
Not necessarily. They make it possible to introduce the check gradually in an existing project. But they must be specific, justified, and gradually reduced; otherwise, they merely hide violations.
How I use architectural boundaries in practice
I protect architecture only where it makes safe changes easier.
In the public scheduling project, modules are divided into Domain, Application, Infrastructure, and UI. The Deptrac configuration verifies selected dependencies; it is a concrete example, not a universal template for every application.