Clean Architecture
Architecture logicielle prônant l'indépendance des composants métier vis-à-vis des frameworks, bases de données et interfaces utilisateur.
Mis à jour le 27 février 2026
La Clean Architecture, conceptualisée par Robert C. Martin (Uncle Bob), est une approche architecturale qui structure les applications en couches concentriques où les dépendances pointent toujours vers l'intérieur. Son principe fondamental repose sur l'isolation du cœur métier des détails techniques, garantissant ainsi une maintenabilité optimale et une testabilité maximale. Cette architecture favorise l'évolution technologique sans refonte complète du système.
Fondements
- Règle de dépendance : les couches internes ne connaissent jamais les couches externes
- Séparation des préoccupations : chaque couche a une responsabilité clairement définie
- Inversion de dépendance : les abstractions gouvernent les implémentations concrètes
- Indépendance technologique : le métier ne dépend d'aucun framework ou outil externe
Avantages
- Testabilité accrue : le cœur métier peut être testé sans infrastructure ni UI
- Évolutivité technique : changement de framework ou BDD sans impacter la logique métier
- Maintenabilité supérieure : modifications localisées grâce aux frontières claires
- Indépendance des équipes : développement parallèle sur différentes couches
- Réutilisabilité du code : logique métier portable vers d'autres projets
Exemple concret
Voici une implémentation TypeScript illustrant les couches de la Clean Architecture pour un système de gestion de commandes :
// Couche Entités (cœur métier)
export class Order {
constructor(
public readonly id: string,
public readonly customerId: string,
private items: OrderItem[],
private _status: OrderStatus
) {}
addItem(item: OrderItem): void {
if (this._status !== OrderStatus.DRAFT) {
throw new Error('Cannot modify confirmed order');
}
this.items.push(item);
}
get totalAmount(): number {
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
confirm(): void {
if (this.items.length === 0) {
throw new Error('Cannot confirm empty order');
}
this._status = OrderStatus.CONFIRMED;
}
}// Couche Cas d'usage
export interface OrderRepository {
findById(id: string): Promise<Order | null>;
save(order: Order): Promise<void>;
}
export interface NotificationService {
sendConfirmation(customerId: string, orderId: string): Promise<void>;
}
export class ConfirmOrderUseCase {
constructor(
private orderRepo: OrderRepository,
private notifications: NotificationService
) {}
async execute(orderId: string): Promise<void> {
const order = await this.orderRepo.findById(orderId);
if (!order) throw new Error('Order not found');
order.confirm();
await this.orderRepo.save(order);
await this.notifications.sendConfirmation(order.customerId, orderId);
}
}// Couche Infrastructure (adaptateur)
import { OrderRepository } from '@/application/use-cases/ConfirmOrder';
import { Order } from '@/domain/entities/Order';
import { Pool } from 'pg';
export class PostgresOrderRepository implements OrderRepository {
constructor(private db: Pool) {}
async findById(id: string): Promise<Order | null> {
const result = await this.db.query(
'SELECT * FROM orders WHERE id = $1',
[id]
);
return result.rows[0] ? this.toDomain(result.rows[0]) : null;
}
async save(order: Order): Promise<void> {
const data = this.toPersistence(order);
await this.db.query(
'UPDATE orders SET status = $1, items = $2 WHERE id = $3',
[data.status, JSON.stringify(data.items), order.id]
);
}
private toDomain(raw: any): Order { /* mapping */ }
private toPersistence(order: Order): any { /* mapping */ }
}Mise en œuvre
- Identifier les entités métier et leurs règles de gestion invariantes
- Définir les cas d'usage représentant les intentions utilisateur
- Créer les interfaces (ports) pour les dépendances externes
- Implémenter les adaptateurs (repositories, controllers, gateways)
- Configurer l'injection de dépendances en respectant la règle de dépendance
- Structurer les répertoires selon les couches (domain, application, infrastructure, presentation)
Conseil professionnel
Commencez toujours par modéliser votre domaine métier avant de choisir vos outils techniques. Un bon test : si vous pouvez exécuter vos tests unitaires des entités et cas d'usage sans base de données, framework web ou librairie externe, vous êtes sur la bonne voie. La Clean Architecture excelle dans les systèmes complexes à longue durée de vie où le métier évolue fréquemment.
Outils associés
- NestJS avec sa structure modulaire adaptée aux architectures en couches
- TypeORM ou Prisma comme adaptateurs de persistance interchangeables
- InversifyJS pour l'injection de dépendances sophistiquée
- Jest avec des configurations séparées par couche pour les tests
- Nx ou Turborepo pour gérer les monorepos multi-couches
- ArchUnit (Java) ou ts-arch pour valider les règles architecturales
La Clean Architecture représente un investissement stratégique pour les projets d'envergure nécessitant agilité technique et pérennité. En isolant la logique métier des détails d'implémentation, elle permet aux équipes de pivoter technologiquement sans remettre en cause la valeur métier accumulée. Cette approche transforme le code en actif stratégique plutôt qu'en dette technique, garantissant un ROI optimal sur le long terme.

