How to Apply Clean Architecture in Laravel

PUBLISHED: 2026-07-22
AUTHOR: MANUEL PRIETO
Architecture

Bringing the theoretical concepts of Clean Architecture to a modern PHP framework like Laravel can seem challenging due to the natural coupling promoted by Eloquent's Active Record pattern. However, designing a strict separation is highly achievable to guarantee that our business core remains independent of the framework and 100% testable.

To do this, we apply the Dependency RuleGlosarioRegla de DependenciaPrincipio central de la Arquitectura Limpia que establece que las dependencias del código fuente solo pueden apuntar hacia adentro, es decir, hacia las capas que contienen la lógica de negocio y las reglas empresariales (Entidades y Casos de Uso). Ningún componente o clase de un círculo interno puede conocer, importar o hacer referencia directa a elementos ubicados en círculos externos (como frameworks, bases de datos o la interfaz de usuario).Ver término completo →: all source code dependencies must point inwards, isolating Laravel and its database in the system's outer circles. If you need to review the theoretical foundations first, we recommend reading our handbook on Clean Architecture Foundations.

Organizing Layers in the Directory Structure

To structure the layers inside Laravel's app/ folder, we use dedicated namespaces to separate responsibilities:

  • App/Domain: Contains Entities and contract interfaces. This layer is pure business theory and imports absolutely nothing from Laravel.
  • App/Application: Contains Use Cases (application services) and data transfer objects (DTOs).
  • App/Infrastructure: Contains the concrete implementation of data persistence (like Eloquent), external APIs, and HTTP controllers.

Implementing the Core: Domain Entities & Contracts

The innermost layer is the Domain. Here, no database models or framework imports exist. We define a pure business Entity in PHP:

Next, we define the contract that the application will need to search and save users. This contract acts as an interface applying the dependency inversion principle of SOLIDGlosarioSOLIDAcrónimo de cinco principios de diseño orientado a objetos y programación diseñados para hacer que el software sea más comprensible, flexible y mantenible: S - Single Responsibility Principle (Responsabilidad Única) O - Open/Closed Principle (Abierto/Cerrado) L - Liskov Substitution Principle (Sustitución de Liskov) I - Interface Segregation Principle (Segregación de Interfaz) D - Dependency Inversion Principle (Inversión de Dependencias) Ver término completo →:

Orchestration: Use Cases and DTOs

In the application layer, we build the Use Case. It orchestrates the business flow by calling repositories and interacting with entities. To pass data safely between outer layers and use cases, we implement a simple Data Transfer Object (DTO):

Now we create the Use Case responsible for registering the user, applying the DRYGlosarioDRY (Don't Repeat Yourself)Principio fundamental de diseño de software formulado por Andy Hunt y Dave Thomas. Establece que toda pieza de conocimiento o lógica debe tener una representación única, no ambigua y definitiva en el sistema para evitar duplicidad y facilitar el mantenimiento.Ver término completo → principle by encapsulating this logic in a single place:

Adapters & Infrastructure: Laravel Models & Repositories

To satisfy Clean Architecture in Laravel, we treat Eloquent models and physical tables as external details of the infrastructure circle:

  1. Eloquent Models: Models in app/Models belong to the Frameworks & Drivers layer and connect to the external database.
  2. Concrete Repositories: We create a concrete repository in the infrastructure layer that implements the domain interface. It handles persistence using Eloquent and maps Eloquent models to pure domain entities.

Binding Services

To let Laravel automatically inject the correct EloquentUserRepository whenever a class requests UserRepositoryInterface, we bind them in app/Providers/AppServiceProvider.php:

Entry Points: HTTP Controllers

The controller is part of the outer circle. It receives the request, validates the input, instantiates the DTO, and triggers the Use Case:

Structuring our PHP applications this way allows us to fully isolate the core business logic from framework and infrastructure choices, ensuring system scalability and making unit testing straightforward.