Philosophy
The primary goal is to decouple business logic (the Domain) from technical implementation (Infrastructure and UI), while keeping each bounded context independently replaceable.
Three pillars
- Domain-centricity — Business rules stay pure. Domain code carries zero dependencies on a host framework, database driver, or third-party SDK.
- Modularity (bounded contexts) — The application is divided into independent modules (e.g.
Ordering,Warehouse,Directory). Each module is a self-contained unit with its own Application, Domain, Infrastructure, and UI. - Dependency inversion — The Domain defines ports (interfaces); Infrastructure provides adapters (implementations). Application orchestrates; it never reaches past a port into a concrete driver.
The composition root binds each Domain port to an Infrastructure adapter. Domain never imports the host container API. How that wiring is expressed is a host detail — still no business rules in the composition root.
Composition root (wiring only — no business rules)
| Host | Typical place |
|---|---|
| Laravel | Module `ServiceProvider` + `bootstrap/providers.php` |
| Symfony | `config/services*.yaml` + bundle Extension |
| Yii | DI config / provider classes (Yii3) |
| CodeIgniter | `Config/Services.php` + module bootstrap |
| CakePHP | Plugin `services()` / container definitions |
| Spiral | Bootloaders |
| Slim | PHP-DI (or PSR-11) definitions + thin `index.php` |
| Mezzio | `ConfigProvider` + chosen DI container |
Full topic pages: Adapters overview
Hexagonal Architecture in one sentence
Use Cases talk to the outside world only through ports. Adapters implement those ports for databases, HTTP peers, queues, clocks, and UI delivery mechanisms.
Domain-Driven Design in this standard
- Entities / value objects / domain services express invariants inside a module.
- Application Use Cases orchestrate a single user intention.
- Bounded contexts are module boundaries — not namespace cosmetics.
- Anti-Corruption Layers and Domain Events are the only sanctioned bridges between modules.
What Core is not
| Not Core | Where it belongs |
|---|---|
| ORM models, query builders | Adapter → persistence |
| Framework service providers / DI container APIs | Adapter → composition root |
| Admin panel widgets | Adapter → UI |
| Linters, arch-test runners, generators | Adapter → tooling |
| Product feature catalogs | Your application roadmap |
Replaceability is the product
A module that can only work when another module’s Domain is on the classpath is not modular — it is a distributed monolith with folders.
Core optimizes for this outcome:
- Extract a module into an internal package later.
- Replace a peer with an HTTP API without rewriting Use Cases.
- Swap persistence drivers behind the same repository port.
Continue with module layout.