Clean Architecture : construire un logiciel qui survit à son framework
La Clean Architecture, ce n'est pas une histoire de dossiers ou de diagrammes,c'est une seule règle : les dépendances pointent vers l'intérieur. Voici ce que ça change vraiment dans un vrai codebase.
La Clean Architecture de Robert C. Martin a popularisé un schéma de cercles concentriques, mais le schéma n'est pas l'essentiel. L'essentiel est une seule règle qui, appliquée avec constance, garde votre logique métier utilisable longtemps après que le framework dans lequel vous l'avez construite ait été remplacé.
La règle de dépendance
Les dépendances du code source ne peuvent pointer que vers l'intérieur. Les couches externes,framework web, base de données, UI,dépendent des couches internes : use cases et entities. L'inverse n'est jamais autorisé. Un use case ne doit jamais importer un modèle Eloquent, un contrôleur Symfony, ou un client HTTP directement.
Les couches, de l'intérieur vers l'extérieur
- ✓Entities : règles métier valables pour toute l'entreprise, qui existeraient même sans cette application spécifique,agnostiques du framework, aucun import en dehors de code en langage pur.
- ✓Use Cases : règles métier spécifiques à l'application qui orchestrent les entities pour répondre à une requête,toujours aucune dépendance au framework.
- ✓Interface Adapters : controllers, presenters et gateways qui convertissent les données entre le format du use case et ce dont la couche externe a besoin (HTTP, SQL, JSON).
- ✓Frameworks & Drivers : la base de données, le framework web, l'UI,la couche la plus volatile, et celle qui devrait être la plus facile à remplacer.
Un exemple concret : découpler un use case de Laravel
Un use case doit dépendre d'une interface, pas d'Eloquent. L'implémentation concrète vit dans la couche externe et est liée au runtime.
// Use case : PHP pur, aucun Eloquent, aucun import de framework
final class PlaceOrder
{
public function __construct(
private OrderRepository $orders, // interface, pas Eloquent
private PaymentGateway $payments, // interface
) {}
public function execute(PlaceOrderRequest $request): Order
{
$order = Order::create($request->items, $request->customerId);
$this->payments->charge($order->total(), $request->paymentToken);
$this->orders->save($order);
return $order;
}
}// Couche domaine : définit le contrat
interface OrderRepository
{
public function save(Order $order): void;
public function find(string $id): ?Order;
}
// Couche infrastructure : l'implémente avec Eloquent
final class EloquentOrderRepository implements OrderRepository
{
public function save(Order $order): void
{
OrderModel::updateOrCreate(['id' => $order->id()], $order->toArray());
}
public function find(string $id): ?Order
{
$model = OrderModel::find($id);
return $model ? Order::fromModel($model) : null;
}
}
// Lié dans le service container, jamais référencé directement par le use case
$this->app->bind(OrderRepository::class, EloquentOrderRepository::class);Ce que ça apporte, et ce que ça coûte
- ✓Testabilité : le use case peut être testé avec un repository en mémoire, sans base de données ni serveur HTTP.
- ✓Infrastructure interchangeable : changer d'ORM, de base de données, voire de framework sans toucher une seule règle métier.
- ✓Un vrai coût : plus de fichiers, plus d'indirection, et une courbe d'apprentissage pour l'équipe.
N'appliquez pas la Clean Architecture à tous les projets. Elle est rentable quand la logique métier est complexe, durable, ou doit être testée et remplacée indépendamment de l'infrastructure. Un panel admin CRUD basique n'a pas besoin de quatre couches et d'une interface pour chaque repository.
Même sans adopter toute la structure de dossiers, la règle à garder est simple : ne laissez pas les classes du framework s'infiltrer dans votre logique métier. Cette seule discipline évite l'essentiel des douleurs que la Clean Architecture est censée résoudre.
Besoin d'aide sur ce sujet ? Développement Full Stack
Découvrir ce service →