Retour aux articles

Application de la Clean Architecture dans un projet Full Stack Spring Boot et Angular

Application de la Clean Architecture dans un projet Full Stack Spring Boot et Angular | Laty Gueye Samba - Développeur Full Stack Dakar Sénégal, Expert Java Spring Boot Angular
```html

Application de la Clean Architecture dans un projet Full Stack Spring Boot et Angular

La Clean Architecture permet de structurer un système logiciel en couches strictement séparées. Dans un projet Full Stack mêlant Spring Boot (backend) et Angular (frontend), cette approche améliore la maintenabilité, réduit le couplage et facilite les tests. L’objectif principal consiste à préserver l’indépendance du métier face aux détails techniques (base de données, framework web, HTTP, etc.).

Principes clés de la Clean Architecture

Une architecture Clean s’appuie sur quelques règles fondamentales :

  • Dépendances orientées : le domaine ne dépend pas des outils externes.
  • Règles de métier au centre : la logique fonctionnelle reste prioritaire.
  • Ports & Adapters : les dépendances externes sont encapsulées via interfaces.
  • Testabilité : les cas d’usage peuvent être testés sans serveur, sans DB réelle.

Découpage recommandé côté backend (Spring Boot)

Le backend peut être organisé autour de quatre ensembles principaux :

1) Domain (cœur métier)

Le Domain contient les entités, les objets de valeur et les règles de métier. Aucun import Spring, aucun accès direct à une base de données.

2) Application (cas d’usage)

Le module Application implémente les use cases. Il orchestre le domaine et s’appuie sur des interfaces (ports) pour accéder aux données ou à d’autres services.

3) Infrastructure (implémentations techniques)

Le module Infrastructure fournit les implémentations concrètes : repositories JPA, clients HTTP, gestion des transactions, etc. Il implémente les ports définis côté Application.

4) Interfaces (web / contrôleurs)

Le module d’Interfaces expose les endpoints HTTP via Spring MVC. Il convertit la requête HTTP en modèle d’entrée des cas d’usage, puis transforme la réponse en format API.

Modélisation Ports & Adapters

Les ports sont des interfaces décrivant ce dont l’application a besoin. Les adapters sont des implémentations concrètes (DB, API, etc.).

Exemple de port (Application)

// Port : Application layer
public interface UserRepositoryPort {
    Optional<User> findById(UUID id);
    User save(User user);
}

Exemple d’adapter (Infrastructure)

// Adapter : Infrastructure layer
@Repository
public class JpaUserRepositoryAdapter implements UserRepositoryPort {

    private final UserJpaRepository jpaRepository;

    public JpaUserRepositoryAdapter(UserJpaRepository jpaRepository) {
        this.jpaRepository = jpaRepository;
    }

    @Override
    public Optional<User> findById(UUID id) {
        return jpaRepository.findById(id).map(UserMapper::toDomain);
    }

    @Override
    public User save(User user) {
        var entity = UserMapper.toEntity(user);
        return UserMapper.toDomain(jpaRepository.save(entity));
    }
}

Organisation de l’arborescence du projet

Une structure typique en Maven/Gradle peut ressembler à :

backend/
  domain/
    src/main/java/...
  application/
    src/main/java/...
  infrastructure/
    src/main/java/...
  interfaces/
    src/main/java/...

Cette découpe permet de limiter les dépendances : les modules proches du centre ne dépendent jamais des couches externes.

Cas d’usage et orchestration

Les cas d’usage portent la logique d’application. Ils valident les règles, appellent le domaine et sollicitent les ports.

Exemple de use case (Application)

// Application layer
public class RegisterUserUseCase {

    private final UserRepositoryPort userRepositoryPort;

    public RegisterUserUseCase(UserRepositoryPort userRepositoryPort) {
        this.userRepositoryPort = userRepositoryPort;
    }

    public UserDto execute(RegisterUserCommand command) {
        var user = User.create(command.email(), command.password());
        var saved = userRepositoryPort.save(user);
        return UserDto.from(saved);
    }
}

Le use case reste indépendant de Spring, de JPA et de la couche HTTP.

Exposition HTTP avec des contrôleurs dédiés (Interfaces)

Les contrôleurs Spring agissent comme des adapters inbound : ils traduisent HTTP → commande d’application, et résultat → réponse API.

Exemple de contrôleur Spring (Interfaces)

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final RegisterUserUseCase registerUserUseCase;

    public UserController(RegisterUserUseCase registerUserUseCase) {
        this.registerUserUseCase = registerUserUseCase;
    }

    @PostMapping
    public ResponseEntity<UserDto> register(@RequestBody RegisterUserRequest request) {
        var command = new RegisterUserCommand(request.email(), request.password());
        var result = registerUserUseCase.execute(command);
        return ResponseEntity.status(HttpStatus.CREATED).body(result);
    }
}

Stratégie Côté Angular : Clean Architecture “frontend”

Bien que Clean Architecture soit initialement pensée pour backend, l’idée peut être appliquée au frontend Angular en séparant :

  • Domain/Models : types métier, règles simples, invariants.
  • Use Cases : orchestrations côté UI (ex : chargement, validation, mapping).
  • Gateways : abstraction des appels réseau (HTTP) via interfaces.
  • UI : composants et vues Angular, sans logique métier lourde.

Exemple de gateway (Angular)

// gateway/user.gateway.ts
export interface UserGateway {
  register(payload: { email: string; password: string }): Promise<UserDto>;
}

Implémentation HTTP (Adapter)

// gateway/user.http-adapter.ts
@Injectable({ providedIn: 'root' })
export class UserHttpAdapter implements UserGateway {

  constructor(private http: HttpClient) {}

  register(payload: { email: string; password: string }): Promise<UserDto> {
    return this.http.post<UserDto>('/api/users', payload).toPromise();
  }
}

Use case côté Angular

// use-cases/register-user.usecase.ts
export class RegisterUserUseCase {
  constructor(private userGateway: UserGateway) {}

  async execute(command: { email: string; password: string }): Promise<UserDto> {
    // validation métier légère possible
    return this.userGateway.register(command);
  }
}

Les composants se concentrent sur la présentation : appels à des use cases, gestion de l’état, affichage d’erreurs.

Gestion des DTO et mapping

Un point critique consiste à limiter la “fuite” des objets HTTP (DTO) vers le domaine. Les stratégies courantes :

  • Mapper explicite entre entités domaine et DTO d’API.
  • Command/Query distincts des modèles d’UI et de persistance.
  • Contraintes : les règles métier restent dans le domaine (ex : invariants d’un agrégat).

Avantages concrets dans un projet Spring Boot + Angular

Appliquer la Clean Architecture dans ce contexte procure :

  • Réduction du couplage : les contrôleurs et repos peuvent évoluer sans impacter le domaine.
  • Tests plus simples : les use cases sont testés via ports avec implémentations mock.
  • Évolutivité : l’ajout d’un nouveau transport (ex : messaging) devient plus direct.
  • Lisibilité : la structure du projet reflète le métier avant l’infrastructure.

Bonnes pratiques de mise en œuvre

Configurer l’injection de dépendances sans polluer le cœur

Le cœur (Domain/Application) doit rester indépendant. Les frameworks (Spring/Angular DI) injectent uniquement les adapters dans les use cases ou contrôleurs.

Éviter l’accès direct aux entités JPA depuis le domaine

Le domaine doit manipuler ses propres types. Les entités JPA appartiennent à l’infrastructure.

Centraliser l’API contractuelle

Une couche de mapping garantit que les changements d’API n’impactent pas directement la logique métier.

Conclusion

La Clean Architecture offre un cadre robuste pour structurer un projet Full Stack Spring Boot et Angular. En séparant Domain, Application, Infrastructure et Interfaces, le métier demeure stable face aux changements techniques. Côté Angular, des gateways et use cases similaires permettent de maintenir une cohérence globale et une meilleure maintenabilité du code.

À 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