Implémentation de la Clean Architecture dans une application Spring Boot
Introduction
Cet article présente une méthode pratique pour appliquer la Clean Architecture dans une application Spring Boot. L'approche favorise la séparation des préoccupations, la testabilité et la maintenabilité. Les recommandations ci‑dessous visent à traduire les principes architecturaux en organisation de packages, contrats d'interfaces et exemples de code adaptés à l'écosystème Spring.
Principes essentiels
La Clean Architecture repose sur des couches concentriques et une règle simple : les dépendances pointent vers l'intérieur. Les couches typiques sont : Entities (domaine), Use Cases (cas d'utilisation), Interface Adapters (contrôleurs, presenters, gateways) et Frameworks & Drivers (Spring, base de données, UI). Chaque couche doit rester indépendante des détails d'implémentation externes.
Organisation des packages
Une organisation recommandée pour un projet Spring Boot :
com.example.app
├─ domain
│ ├─ model
│ └─ service (domain services)
├─ usecase
├─ adapter
│ ├─ incoming (rest, graphql)
│ └─ outgoing (persistence, external clients)
└─ configuration
Cette structure permet de mapper directement les responsabilités et d'éviter les fuites de dépendances vers les frameworks.
Définir les entités et les ports (interfaces)
Les entités représentent le modèle métier sans dépendance à Spring. Les ports sont des interfaces qui exposent les capacités nécessaires pour les cas d'utilisation.
// Exemple d'entité
package com.example.app.domain.model;
public class Order {
private Long id;
private String status;
// getters / setters / logique métier minimale
}
// Port (interface) pour la persistence
package com.example.app.usecase.port;
public interface OrderRepository {
Order findById(Long id);
Order save(Order order);
}
Implémenter les cas d'utilisation
Les cas d'utilisation orchestrent la logique métier en s'appuyant uniquement sur des ports. Ils restent indépendants des frameworks.
package com.example.app.usecase;
public class ProcessOrderUseCase {
private final com.example.app.usecase.port.OrderRepository repo;
public ProcessOrderUseCase(com.example.app.usecase.port.OrderRepository repo) {
this.repo = repo;
}
public void execute(Long orderId) {
Order order = repo.findById(orderId);
// logique de traitement
repo.save(order);
}
}
Adapters : contrôleurs et persistence
Les adapters implémentent les ports et traduisent les modèles entre couches. Dans Spring Boot, les contrôleurs REST et les repositories JPA se trouvent dans cette couche.
package com.example.app.adapter.out.persistence;
@Repository
public class OrderRepositoryJpa implements com.example.app.usecase.port.OrderRepository {
// utilisation d'un Spring Data JPA interne ou EntityManager
}
package com.example.app.adapter.incoming.web;
@RestController
public class OrderController {
private final com.example.app.usecase.ProcessOrderUseCase useCase;
// endpoints exposés
}
Configuration et injection de dépendances
La configuration Spring assemble les dépendances en liant les implémentations d'adapters aux ports définis par le domaine ou les cas d'utilisation. Les annotations @Configuration et @Bean ou l'injection par constructeur sont utilisées sans faire dépendre le domaine de Spring.
@Configuration
public class UseCaseConfig {
@Bean
public ProcessOrderUseCase processOrderUseCase(OrderRepository orderRepository) {
return new ProcessOrderUseCase(orderRepository);
}
}
Tests
La séparation claire permet d'écrire des tests unitaires pour les cas d'utilisation sans démarrer Spring. Les adapters peuvent être testés avec des tests d'intégration Spring Boot en isolant la couche d'infrastructure.
Bonnes pratiques et pièges à éviter
Quelques recommandations pratiques :
Séparer clairement les packages pour éviter les dépendances cycliques.
Utiliser des interfaces comme contrats entre couches et maintenir le domaine exempt de dépendances Spring.
Préserver la règle des dépendances : les détails (frameworks, DB) ne doivent jamais influencer les modèles du domaine.
Pièges courants : mélanger logique de présentation dans les cas d'utilisation, ou injecter des composants Spring directement dans les entités.
Conclusion
L'application des principes de la Clean Architecture dans un projet Spring Boot améliore la robustesse et facilite l'évolution. L'approche demande une discipline initiale dans la structuration du code et la définition des interfaces, mais apporte des bénéfices significatifs en testabilité et compréhension du système.
À propos de l'auteur
Laty Gueye Samba est développeur Full Stack basé à Dakar, Sénégal. Spécialiste des écosystèmes Java / Spring Boot et Angular.
Contact : latygueyesamba@gmail.com | Dakar, Sénégal