Retour aux articles

Mettre en œuvre la Clean Architecture dans une application Full Stack Spring Boot et Angular

Mettre en œuvre la Clean Architecture dans une application Full Stack Spring Boot et Angular | Laty Gueye Samba - Développeur Full Stack Dakar Sénégal, Expert Java Spring Boot Angular

Dans le monde du développement logiciel, où la complexité des applications croît sans cesse, la mise en œuvre de principes d'architecture solides est devenue une nécessité. La Clean Architecture, popularisée par Robert C. Martin (Uncle Bob), offre une approche structurée pour construire des systèmes robustes, testables et maintenables. Pour un Développeur Full Stack Java Spring Boot + Angular comme Laty Gueye Samba, basé à Dakar, comprendre et appliquer ces principes est fondamental pour livrer des solutions de haute qualité.

Cet article explore comment la Clean Architecture peut être appliquée efficacement dans une application Full Stack, en séparant clairement les préoccupations au sein des parties backend (Spring Boot) et frontend (Angular). L'objectif est de démontrer comment ces Design Patterns peuvent améliorer la flexibilité, la scalabilité et la durée de vie des projets, qu'il s'agisse de systèmes ERP ou d'applications de gestion des risques, un domaine dans lequel l'expertise de Laty Gueye Samba est particulièrement pertinente.

L'adoption de la Clean Architecture ne se limite pas à une simple organisation de code ; elle représente une philosophie de conception qui vise à protéger les règles métier fondamentales des détails d'implémentation. Cela est particulièrement crucial dans des environnements dynamiques, où les technologies évoluent rapidement et où les exigences métier sont en constante adaptation. Un expert Java Spring Boot Angular sait que l'indépendance vis-à-vis des frameworks et des bases de données est une clé de la pérennité.

Les principes fondamentaux de la Clean Architecture

Au cœur de la Clean Architecture se trouve le principe des couches concentriques et la règle de dépendance. Les couches sont organisées de l'intérieur vers l'extérieur, chaque couche externe dépendant de celles qui lui sont internes, mais jamais l'inverse. Cela garantit que les règles métier essentielles restent intactes et indépendantes des frameworks, des bases de données ou des interfaces utilisateur.

  • Entités (Entities) : La couche la plus interne. Elle contient les règles métier de l'entreprise, souvent des objets avec des méthodes qui encapsulent la logique la plus critique. Ce sont des objets métier purs, indépendants de toute application spécifique.
  • Cas d'Utilisation (Use Cases / Interactors) : Cette couche contient les règles métier spécifiques à l'application. Elle orchestre le flux de données vers et depuis les entités et utilise les interfaces des gateways pour communiquer avec la couche externe.
  • Adaptateurs d'Interface (Interface Adapters) : Cette couche convertit les données des cas d'utilisation vers le format le plus pratique pour les frameworks et les bases de données, et vice-versa. Elle inclut les contrôleurs, les présentateurs (DTOs) et les gateways.
  • Frameworks et Pilotes (Frameworks & Drivers) : La couche la plus externe. Elle contient tous les détails d'implémentation, tels que la base de données, le framework web, l'interface utilisateur ou les outils externes.

La règle de dépendance stipule que le code dans les cercles intérieurs ne doit avoir aucune connaissance du code dans les cercles extérieurs. Cela signifie que les entités ne doivent pas connaître les cas d'utilisation, et les cas d'utilisation ne doivent pas connaître les adaptateurs d'interface ou les frameworks. Cette séparation stricte est la pierre angulaire de la testabilité et de la maintenabilité, permettant de changer un framework sans affecter la logique métier principale.

Application de la Clean Architecture à Spring Boot (Backend)

Pour un backend Spring Boot, la mise en œuvre de la Clean Architecture se traduit par une organisation modulaire du code, souvent sous forme de packages ou de modules distincts pour chaque couche. Laty Gueye Samba, en tant qu'expert Java Spring Boot Angular, souligne l'importance de cette structuration pour des projets complexes tels que des applications de gestion hospitalière ou des systèmes ERP.


// 1. Couche Entités (Domain)
package com.latysamba.cleanarch.domain.entity;

public class User {
    private String id;
    private String username;
    private String email;

    // Getters, Setters, Constructor
}

// 2. Couche Cas d'Utilisation (Application) - Interfaces pour les ports d'entrée/sortie
package com.latysamba.cleanarch.application.port.in;

public interface CreateUserUseCase {
    User createUser(String username, String email);
}

package com.latysamba.cleanarch.application.port.out;

public interface UserRepository {
    User save(User user);
    User findById(String id);
}

// Implémentation du Cas d'Utilisation
package com.latysamba.cleanarch.application.usecase;

import com.latysamba.cleanarch.application.port.in.CreateUserUseCase;
import com.latysamba.cleanarch.application.port.out.UserRepository;
import com.latysamba.cleanarch.domain.entity.User;
import org.springframework.stereotype.Service;

@Service
public class CreateUserService implements CreateUserUseCase {
    private final UserRepository userRepository;

    public CreateUserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public User createUser(String username, String email) {
        User user = new User();
        user.setUsername(username);
        user.setEmail(email);
        return userRepository.save(user);
    }
}

// 3. Couche Adaptateurs d'Interface (Infrastructure - Web/Persistence)
package com.latysamba.cleanarch.adapter.web;

import com.latysamba.cleanarch.application.port.in.CreateUserUseCase;
import com.latysamba.cleanarch.domain.entity.User;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {
    private final CreateUserUseCase createUserUseCase;

    public UserController(CreateUserUseCase createUserUseCase) {
        this.createUserUseCase = createUserUseCase;
    }

    @PostMapping
    public ResponseEntity createUser(@RequestBody UserCreationRequest request) {
        User user = createUserUseCase.createUser(request.getUsername(), request.getEmail());
        return ResponseEntity.ok(user);
    }
}

package com.latysamba.cleanarch.adapter.persistence;

import com.latysamba.cleanarch.application.port.out.UserRepository;
import com.latysamba.cleanarch.domain.entity.User;
import org.springframework.stereotype.Repository;

// Cette classe pourrait interagir avec JPA/Hibernate
@Repository
public class UserPersistenceAdapter implements UserRepository {
    // Supposons une implémentation JPA ici
    @Override
    public User save(User user) {
        // Logique de sauvegarde en base de données
        System.out.println("Saving user to DB: " + user.getUsername());
        return user; // Retourne l'utilisateur sauvegardé
    }

    @Override
    public User findById(String id) {
        // Logique de récupération depuis la base de données
        return null;
    }
}

Dans cet exemple, la couche de persistance (UserPersistenceAdapter) implémente une interface (UserRepository) définie dans la couche d'application. Cela inverse la dépendance : la couche d'application dicte ce dont elle a besoin, et la couche d'infrastructure fournit l'implémentation. Le UserController interagit uniquement avec le cas d'utilisation, ignorant les détails de la base de données ou de la logique métier sous-jacente.

Application de la Clean Architecture à Angular (Frontend)

La Clean Architecture peut également apporter une structure bienvenue aux applications frontend Angular, rendant le code plus gérable et plus facile à tester. Bien que les concepts soient similaires, l'implémentation s'adapte à la nature réactive et basée sur les composants d'Angular. Un Développeur Full Stack Dakar Sénégal comme Laty Gueye Samba applique ces principes pour créer des interfaces utilisateur robustes et évolutives.

  • Domaine (Domain) : Définit les interfaces TypeScript pour les entités métier (ex: User, Product). Ces interfaces sont pures et ne dépendent d'aucune implémentation spécifique.
  • Cas d'Utilisation (Application) : Contient les services qui encapsulent la logique métier spécifique à l'application frontend. Ces services interagissent avec la couche d'infrastructure via des interfaces (ports) pour récupérer ou envoyer des données.
  • Présentation (Presentation) : Comprend les composants Angular qui affichent les données à l'utilisateur et gèrent les interactions. Ils communiquent avec les services de la couche "Cas d'Utilisation".
  • Infrastructure (Infrastructure) : Gère l'interaction avec le monde extérieur, comme les appels HTTP à l'API backend, la gestion du stockage local ou les services tiers. Cette couche implémente les interfaces définies par la couche "Cas d'Utilisation".

// 1. Couche Domaine
// src/app/domain/models/user.model.ts
export interface User {
  id: string;
  username: string;
  email: string;
}

// 2. Couche Cas d'Utilisation (Application) - Interfaces
// src/app/application/ports/in/create-user.usecase.ts
import { Observable } from 'rxjs';
import { User } from '../../domain/models/user.model';

export abstract class CreateUserUseCase {
  abstract createUser(username: string, email: string): Observable;
}

// src/app/application/ports/out/user.repository.ts
import { Observable } from 'rxjs';
import { User } from '../../domain/models/user.model';

export abstract class UserRepository {
  abstract saveUser(user: User): Observable;
}

// Implémentation du Cas d'Utilisation
// src/app/application/usecases/create-user.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { User } from '../../domain/models/user.model';
import { CreateUserUseCase } from '../ports/in/create-user.usecase';
import { UserRepository } from '../ports/out/user.repository';

@Injectable({
  providedIn: 'root'
})
export class CreateUserService implements CreateUserUseCase {
  constructor(private userRepository: UserRepository) {} // Injection du port de sortie

  createUser(username: string, email: string): Observable {
    const newUser: User = { id: '', username, email }; // ID sera généré par le backend
    return this.userRepository.saveUser(newUser);
  }
}

// 3. Couche Présentation (UI/Components)
// src/app/presentation/components/user-form/user-form.component.ts
import { Component } from '@angular/core';
import { CreateUserUseCase } from '../../../application/ports/in/create-user.usecase';

@Component({
  selector: 'app-user-form',
  template: `
    <input [(ngModel)]="username" placeholder="Username">
    <input [(ngModel)]="email" placeholder="Email">
    <button (click)="onSubmit()">Create User</button>
  `
})
export class UserFormComponent {
  username: string = '';
  email: string = '';

  constructor(private createUserUseCase: CreateUserUseCase) {}

  onSubmit() {
    this.createUserUseCase.createUser(this.username, this.email).subscribe(
      user => console.log('User created:', user),
      error => console.error('Error creating user:', error)
    );
  }
}

// 4. Couche Infrastructure (API Clients)
// src/app/infrastructure/services/api/user-api.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../../../domain/models/user.model';
import { UserRepository } from '../../../application/ports/out/user.repository'; // Implémente le port

@Injectable({
  providedIn: 'root'
})
export class UserApiService implements UserRepository {
  private apiUrl = 'http://localhost:8080/users';

  constructor(private http: HttpClient) {}

  saveUser(user: User): Observable {
    return this.http.post(this.apiUrl, user);
  }
}

Dans l'exemple Angular, le composant UserFormComponent dépend uniquement de CreateUserUseCase. L'implémentation concrète de l'appel API (UserApiService) est gérée par la couche d'infrastructure et injectée via le mécanisme de dépendance d'Angular, implémentant l'interface UserRepository définie dans la couche d'application. Cette approche garantit une grande découplage et facilite les tests unitaires et d'intégration.

Point de vue : développeur full stack à Dakar

Pour un développeur travaillant sur des systèmes comme des projets de gestion hospitalière ou des applications de gestion des risques complexes, la maîtrise de la Clean Architecture représente un avantage concurrentiel réel sur le marché technologique africain, en pleine expansion. Laty Gueye Samba, Développeur Full Stack à Dakar, observe que cette approche non seulement assure la robustesse et la maintenabilité des applications sur le long terme, mais elle facilite aussi l'intégration de nouvelles fonctionnalités et l'adaptation aux évolutions technologiques, ce qui est crucial dans un environnement aussi dynamique.

Conclusion

La mise en œuvre de la Clean Architecture dans des applications Full Stack Spring Boot et Angular est une stratégie puissante pour construire des systèmes évolutifs, maintenables et résilients. En séparant clairement les préoccupations en couches distinctes, les développeurs peuvent protéger la logique métier critique des changements d'infrastructure ou d'interface utilisateur, réduisant ainsi les coûts de maintenance et augmentant la flexibilité du projet.

Pour un Développeur Full Stack Dakar Sénégal comme Laty Gueye Samba, l'application de ces Design Patterns est essentielle pour répondre aux exigences des projets d'envergure, garantissant que les solutions livrées sont non seulement fonctionnelles, mais aussi architecturalement saines et prêtes pour l'avenir. Adopter la Clean Architecture, c'est investir dans la qualité et la durabilité du logiciel.

Pour approfondir le sujet, il est recommandé de consulter les ressources officielles :

À 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