A little repository to show an example of the clean architecture with Laravel
Some links that were useful to me in implementing this example of clean code architecture as effectively as possible :
- https://laravel-france.com/posts/clean-architecture-laravel
- https://laravel-france.com/posts/qsdqsdqsdq?signature=a640a7807133f052b255529eac549ea122bbda5fc0d61180edf8a65653a60732
- https://github.com/shahghasiadil/laravel-clean-architecture-ddd-cqrs/blob/main/src/Infrastructure/User/Persistence/Repositories/UserRepository.php
- https://github.com/Sprorowski/clean-architecture-laravel/tree/main/app/Infrastructure/Database
- https://github.com/julienlucas/nestjs-cleanarchi-demo/tree/main/src/infrastructure/repositories
This small application is mainly based on migrations and therefore the basic models linked to the framework via laravel/breeze such as the user table.
Clean architecture can consist of different layers depending on the implementation. Here, we're using a classic case where, from the outside to inside with infrastructure and presentation are two layers at the same level but with different uses cases. Both go towards to the application layer, which in turn go toward to the domain layer.
If we go into a little more detail and look at the implementation side only, we can distinguish several stages. Each layer communicates with each other using interfaces. The idea is to protect the core of your application (business rules) from technical details (frameworks, databases, UI, external APIs) by inverting dependencies (inward-facing dependencies) :
- L’interface déclare ce qui doit être fait.
- L’implémentation concrète décide comment.
The path for this part is currently: /app/Infrastructure/Persistence/Eloquent/User/. Tomorrow this could be completely different from the ORM offered by Laravel. So we have the UserRepository and the UserRepositoryInterface which defines a getRandomList method to retrieve a random list of 10 users.
The "Application" layer is responsible for organizing the data via "DTOs" and "mapping" to return the necessary data in the correct format. The path defining the latter is: /app/Application/UseCases/User/.
In the /app/Application/UseCases/ folder, an ActionInterface interface is defined to make it possible to execute the action from the controller. We get a Collection of DTOs also defined beforehand. On the other hand, in /app/Application/UseCases/User/ we define the interface UserActionInterface which extends from ActionInterface. This allows for a clean implementation of user-related business logic. Next is the GetRandomSortUserList class, which allows you to call the repository method via the UserRepositoryInterface interface, enabling you to then map the retrieved data.
- Purpose: Coordinates the use cases of the system — defines what the application does without specifying how the infrastructure works.
- Contents: Use Case Services or Interactors
- Ports: (interfaces for infrastructure implementations)
- Key Principle: Depends only on the Domain Layer; it’s technology-agnostic.
The domain should represent only the business model. Here we only have the model representing the users. There should be no business logic located in the Application part. The path that defines the domain layer is: /app/Domain/Entities/
The last "Presentation" used to return values to display them is separated into two parts:
/app/Presentation/Presenter//app/Presentation/ViewModel/User
From the path /app/Presentation/Presenter we have, as in the Action section, a PresenterInterface interface with a general present method that can be called anywhere while remaining agnostic, particularly on the layer. Then, if we go to the User folder, we have a UserPresenterInterface interface that extends the agnostic interface and allows us to sign a contract with the concrete class UserPresenter which will define the data to be sent to the layer, to the resource layer via $this->viewFactory->make, and the model defined in App\Presentation\ViewModel\User\UserViewModel.
In /app/Presentation/ViewModel/User we have the UserViewModel class, which is actually a DTO with the arguments to be returned to the view.
- Domain → Core business, pure and independent.
- Application → Orchestrates use cases (business logic), talks to domain via entities and services.
- Presentation → Handles input/output, communicates with application layer.
- Infrastructure → Technical implementations, depends on interfaces from the application layer.
For more information and knowledge about this architecture, please refer to the links in the Sources section above.
This is a small explanation in the form of documentation to show the steps related to the implementation of Clean Architecture on this project via the user model. If you think the documentation needs to be completed, feel free to create an issue followed by a PR which will be analyzed in order to be passed if relevant to the project. Additionally, if you think you can add concrete examples that could add value to this small Clean Architecture skeleton, feel free to suggest your ideas to improve it and share it with the developer community afterwards.