Clean Architecture promises maintainable, testable code by separating concerns into concentric layers. But applying it to Laravel can feel like forcing a square peg into a round hole. Here is how we do it pragmatically.
The Four Layers
We organize our Laravel applications into four layers, keeping the dependency rule intact — inner layers never know about outer layers.
- Domain: Entities, value objects, repository interfaces — pure PHP, zero framework dependencies
- Application: Use cases, DTOs, service interfaces — orchestrates domain logic
- Infrastructure: Eloquent models, repository implementations, external API clients
- Presentation: Controllers, form requests, resources, Blade views
// Domain layer — pure PHP, no framework imports
class Order
{
public function __construct(
private OrderId $id,
private CustomerId $customerId,
private Money $total,
private OrderStatus $status,
) {}
}
// Application layer use case
class PlaceOrderUseCase
{
public function __construct(
private OrderRepositoryInterface $orders,
private PaymentGatewayInterface $payment,
) {}
}
Where Laravel Fits
Laravel's IoC container makes dependency injection seamless. Controllers remain thin, services encapsulate business logic, and repositories abstract data access.
"Clean Architecture is not about following rules blindly. It is about protecting your business logic from framework lock-in and making it testable without a database."
Practical Tips
- Start with the simplest structure that works — refactor toward clean architecture
- Use interfaces at package boundaries, not everywhere
- Keep Eloquent models in the infrastructure layer, not the domain
- Write feature tests against use cases, not HTTP endpoints
Testing Benefits
The biggest win of clean architecture is testability. Domain logic can be tested with plain PHPUnit tests that run in milliseconds, without Laravel bootstrapping or database setup.