project-backendlisted
Install: claude install-skill ubermuda/loupe
# Backend: forms, DTOs, Doctrine, controllers, flash messages
## Module boundaries
No code in `src/Module/X/` may import from `src/Module/Y/`. This covers entities, services, controllers and repositories. Each module is an island.
phparkitect enforces this. Run `just arkitect`. Do not suppress a reported violation. Redesign instead.
Three patterns cover a legitimate cross-module need:
- Shared abstraction in the owning module. If module Y extends a base class from module X, add an abstract property hook or interface method to the base in module X. Each subtype implements it in its own module. Callers in module X read the abstract member and never learn the concrete type. Example: `Repository` (Project module) declares `abstract public string $label { get; }`, `GitHubRepository` (GitHub module) implements it, and templates read `project.repository.label`. The Project module imports nothing from GitHub.
- Events. For a side effect that crosses a boundary, dispatch a domain event from the originating module and handle it in the receiving module.
- Shared value objects. A truly generic type, such as a `Slug` value object, may live in the root namespace `src/`.
## Read models
A structured read model consumed by several controllers and templates is an immutable `final readonly` value object with query methods. Do not return an `array{a: …, b: …}` shape.
Promote the array to a value object when it grows past about 2 keys, or when templates start per-element lookups such as `