Laravel Clean Architecture (Real Project Structure)
07.05.2026 ยท dayanch
Most Laravel tutorials stop at controllers, models, and views. That is fine for small demos, but real products need a stronger structure.
Clean Architecture helps you separate business rules from framework details, so your application remains testable, scalable, and easy to maintain.
In this guide, we will build a practical Laravel Clean Architecture mindset for real projects.
Why Clean Architecture in Laravel?
Laravel is powerful, but it is easy to place too much logic inside controllers or Eloquent models. Over time this creates:
- Fat controllers
- Mixed responsibilities
- Difficult testing
- Hard refactoring
Clean Architecture solves this by organizing code around business rules first.
Core Principle
Dependencies must point inward.
- Outer layers (HTTP, DB, framework) can depend on inner layers
- Inner layers (domain rules) must never depend on outer layers
Your business logic should work even if the web framework changes.
Recommended Real Project Structure
A practical Laravel structure can look like this:
app/ Domain/ Post/ Entities/ ValueObjects/ Repositories/ Services/ Application/ Post/ UseCases/ DTOs/ Infrastructure/ Persistence/ Eloquent/ Repositories/ Services/ Interfaces/ Http/ Controllers/ Requests/ Resources/
This keeps framework concerns separate from domain logic.
Layer Responsibilities
1) Domain Layer
Contains pure business rules.
- Entities (core business objects)
- Value Objects (validated immutable values)
- Repository Contracts (interfaces)
- Domain Services (business operations)
No Laravel-specific code should exist here.
2) Application Layer
Contains use cases that orchestrate domain logic.
CreatePostUseCasePublishPostUseCase- Input/Output DTOs
Application layer coordinates work but does not know HTTP or database details.
3) Infrastructure Layer
Contains technical implementations.
- Eloquent repository implementations
- External API clients
- Queue integrations
- Cache adapters
This is where Laravel and third-party tools live.
4) Interface Layer
Contains delivery mechanisms.
- Controllers
- Form Requests
- API Resources
Controllers should be thin: validate input, call use case, return response.
Example Flow: Publish Post
- Controller receives request
- FormRequest validates data
- Controller calls
PublishPostUseCase - Use case uses interface
PostRepository - Infrastructure repository updates via Eloquent
- Resource returns response shape
This flow keeps business decisions in the center and framework at the edges.
Repository Pattern (Practical Use)
In Domain:
interface PostRepository { public function findById(int $id): ?Post; public function save(Post $post): void; }
In Infrastructure:
class EloquentPostRepository implements PostRepository { public function findById(int $id): ?PostModel { return PostModel::find($id); } public function save(PostModel $post): void { $post->save(); } }
Bind interface to implementation in a service provider.
Keep Controllers Thin (Always)
Good controller style:
public function publish(PublishPostRequest $request, PublishPostUseCase $useCase) { $output = $useCase->execute($request->validated()); return new PostResource($output->post); }
No business rules. No heavy query logic. No side-effect chaos.
Where Validation, Authorization, and Mapping Belong
- Validation: Form Requests
- Authorization: Policies
- Response Mapping: API Resources
- Business Decisions: Use Cases + Domain
This separation makes code predictable for teams.
Testing Strategy in Clean Laravel Projects
- Unit tests for Domain and Use Cases (fast and isolated)
- Feature tests for HTTP layer
- Integration tests for Infrastructure adapters when needed
Because logic is separated, tests are easier to write and maintain.
Common Mistakes to Avoid
- Putting business rules directly in controllers
- Treating Eloquent models as the full domain layer
- Skipping interfaces for critical dependencies
- Returning raw models from API endpoints
- Mixing external service calls inside core use cases without abstraction
Incremental Adoption (No Big Bang Refactor)
You do not need to rewrite everything.
Start with one feature:
- Create a Use Case
- Move business logic from controller
- Add repository contract
- Implement contract in infrastructure
- Keep controller as orchestration only
Repeat feature by feature.
Final Thoughts
Laravel and Clean Architecture work very well together when applied pragmatically.
You are not trying to add complexity. You are building clear boundaries so your team can ship faster today and refactor safely tomorrow.
If your project is growing, this structure is one of the best investments you can make.