Retour aux articles

Implémenter la Clean Architecture dans un projet Full Stack Spring Boot et Angular

Implémenter 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
Implémenter la Clean Architecture dans un projet Full Stack Spring Boot et Angular - Laty Gueye Samba

Implémenter la Clean Architecture dans un projet Full Stack Spring Boot et Angular

Dans le monde du développement logiciel, la complexité des systèmes ne cesse de croître, rendant essentielle l'adoption de principes architecturaux robustes. La Clean Architecture, popularisée par Robert C. Martin (Uncle Bob), offre une solution élégante pour construire des applications maintenables, testables et indépendantes des frameworks. Pour un Développeur Full Stack expert en Java Spring Boot et Angular basé à Dakar, comme Laty Gueye Samba, la maîtrise de cette architecture est un atout majeur pour livrer des systèmes de haute qualité.

Cet article explore les principes de la Clean Architecture et propose une approche concrète pour son implémentation dans un projet Full Stack typique, combinant un backend Spring Boot et un frontend Angular. L'objectif est de montrer comment ces principes peuvent être appliqués pour maximiser la séparation des préoccupations, la testabilité et la flexibilité, des qualités indispensables dans le développement d'applications métier complexes ou de systèmes ERP.

Les Principes Fondamentaux de la Clean Architecture

La Clean Architecture repose sur l'idée de séparer le code en couches concentriques, où les dépendances ne peuvent que pointer vers l'intérieur. Au cœur se trouvent les règles métier (Enterprise Business Rules), indépendantes de toute technologie, suivies par les règles d'application (Application Business Rules). Les couches externes sont constituées des adaptateurs d'interface et des frameworks/dispositifs.

Les quatre cercles clés, du plus interne au plus externe, sont :

  • Entities (Entités) : Contiennent les règles métier de l'entreprise. Ce sont des objets métier purs, indépendants de l'application.
  • Use Cases (Cas d'Utilisation) : Contiennent les règles métier spécifiques à l'application. Ils orchestrent le flux de données vers et depuis les entités et dirigent les entités pour accomplir leur tâche.
  • Interface Adapters (Adaptateurs d'Interface) : Convertissent les données des Use Cases dans un format compréhensible par la couche la plus externe (et vice-versa). On y trouve les Presenters, Gateways, Controllers, etc.
  • Frameworks & Devices (Frameworks et Dispositifs) : La couche la plus externe, composée de la base de données, du framework web, de l'UI, etc. Elle dépend des couches internes.

La règle fondamentale est la Dependency Rule : les dépendances ne doivent pointer que vers les couches internes. Aucune couche interne ne doit avoir connaissance d'une couche externe. Cette règle garantit l'indépendance du cœur métier.

Application de la Clean Architecture à Spring Boot (Backend)

Pour un backend développé avec Spring Boot, la Clean Architecture se traduit par une structuration modulaire claire. L'expertise d d'un Développeur Full Stack Dakar Sénégal comme Laty Gueye Samba est précieuse pour implémenter cette séparation efficacement, notamment dans des projets de gestion hospitalière ou des applications de gestion des risques.

Une organisation typique pourrait ressembler à ceci :


src/main/java/com/latygueyesamba/project
├── domain               (Entities)
│   ├── model
│   │   └── Product.java
│   └── port
│       ├── in           (Use Case interfaces)
│       │   ├── CreateProductUseCase.java
│       │   └── GetProductQuery.java
│       └── out          (Gateway interfaces - Repositories, external services)
│           └── ProductRepositoryPort.java
├── application          (Use Cases implementations)
│   └── service
│       ├── CreateProductService.java
│       └── GetProductService.java
└── infrastructure       (Interface Adapters, Frameworks & Devices)
    ├── adapter
    │   ├── persistence  (Repository implementations)
    │   │   ├── ProductJpaEntity.java
    │   │   └── ProductJpaRepositoryAdapter.java
    │   └── web          (Controllers)
    │       ├── ProductController.java
    │       └── dto
    │           └── ProductRequest.java
    └── config
        └── BeanConfiguration.java
    

Exemples de code :

1. Entité (Domain Layer) : Pure POJO, sans dépendance Spring ou JPA.


package com.latygueyesamba.project.domain.model;

public class Product {
    private Long id;
    private String name;
    private double price;

    public Product(Long id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }

    public boolean isValidPrice() {
        return this.price > 0;
    }
}
    

2. Cas d'utilisation (Application Layer) : Une interface dans le port in du domaine, implémentée dans la couche application.


// domain/port/in/CreateProductUseCase.java
package com.latygueyesamba.project.domain.port.in;

import com.latygueyesamba.project.domain.model.Product;

public interface CreateProductUseCase {
    Product createProduct(Product product);
}

// application/service/CreateProductService.java
package com.latygueyesamba.project.application.service;

import com.latygueyesamba.project.domain.model.Product;
import com.latygueyesamba.project.domain.port.in.CreateProductUseCase;
import com.latygueyesamba.project.domain.port.out.ProductRepositoryPort;
import org.springframework.stereotype.Service;

@Service
public class CreateProductService implements CreateProductUseCase {

    private final ProductRepositoryPort productRepositoryPort;

    public CreateProductService(ProductRepositoryPort productRepositoryPort) {
        this.productRepositoryPort = productRepositoryPort;
    }

    @Override
    public Product createProduct(Product product) {
        if (!product.isValidPrice()) {
            throw new IllegalArgumentException("Product price must be positive.");
        }
        return productRepositoryPort.save(product);
    }
}
    

Intégrer la Clean Architecture avec Angular (Frontend)

Sur le frontend, Angular peut également bénéficier des principes de la Clean Architecture. L'objectif est de découpler la logique métier de l'interface utilisateur et des appels API, améliorant ainsi la testabilité et la maintenabilité des composants. Un Développeur Full Stack maîtrisant Angular comme Laty Gueye Samba applique ces principes pour des interfaces utilisateur robustes.

Une structure de projet Angular inspirée de la Clean Architecture pourrait inclure :

  • Domain : Contient les interfaces TypeScript pour les modèles de données (par exemple, product.model.ts).
  • Application (Use Cases/Interactors) : Des services qui encapsulent la logique d'application spécifique, manipulent les modèles et interagissent avec les adaptateurs de données. Par exemple, un ProductInteractorService.
  • Infrastructure (Adapters) :
    • Data : Services qui gèrent la communication avec le backend (via HttpClient). Ceux-ci implémentent les "ports de sortie" du domaine ou de l'application. Par exemple, un ProductApiService.
    • Presentation : Composants Angular (dumb components) qui se concentrent sur l'affichage et la gestion des interactions utilisateur, en déléguant la logique métier aux interactors/services.

Exemples de code conceptuels pour Angular :

1. Modèle (Domain Layer) :


// src/app/domain/models/product.model.ts
export interface Product {
  id: number;
  name: string;
  price: number;
}
    

2. Service d'API (Infrastructure - Data Adapter) :


// src/app/infrastructure/data/product-api.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Product } from '../../domain/models/product.model';

@Injectable({
  providedIn: 'root'
})
export class ProductApiService {
  private apiUrl = '/api/products'; // Supposons un proxy pour éviter les problèmes CORS

  constructor(private http: HttpClient) { }

  getProducts(): Observable<Product[]> {
    return this.http.get<Product[]>(this.apiUrl);
  }

  createProduct(product: Product): Observable<Product> {
    return this.http.post<Product>(this.apiUrl, product);
  }
}
    

3. Interactor/Use Case (Application Layer) :


// src/app/application/interactors/product.interactor.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Product } from '../../domain/models/product.model';
import { ProductApiService } from '../../infrastructure/data/product-api.service';

@Injectable({
  providedIn: 'root'
})
export class ProductInteractor {

  constructor(private productApiService: ProductApiService) { }

  loadAllProducts(): Observable<Product[]> {
    return this.productApiService.getProducts();
  }

  addProduct(product: Product): Observable<Product> {
    // Ici, vous pourriez ajouter des règles métier côté client si nécessaire
    return this.productApiService.createProduct(product);
  }
}
    

4. Composant (Infrastructure - Presentation) :


// src/app/infrastructure/presentation/product-list/product-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Product } from '../../../domain/models/product.model';
import { ProductInteractor } from '../../../application/interactors/product.interactor';

@Component({
  selector: 'app-product-list',
  template: `
    <h2>Liste des Produits</h2>
    <ul>
      <li *ngFor="let product of (products$ | async)">
        {{ product.name }} - {{ product.price | currency:'XOF' }}
      </li>
    </ul>
  `
})
export class ProductListComponent implements OnInit {
  products$: Observable<Product[]>;

  constructor(private productInteractor: ProductInteractor) { }

  ngOnInit(): void {
    this.products$ = this.productInteractor.loadAllProducts();
  }
}
    

Point de vue : développeur full stack à Dakar

Pour un développeur travaillant sur des systèmes comme les plateformes de gestion publique ou les applications financières complexes, la maîtrise de la Clean Architecture représente un avantage concurrentiel réel sur le marché technologique africain, en pleine expansion. L'adoption d'une architecture logicielle propre permet de construire des solutions durables, évolutives et facilement maintenables, essentielles pour répondre aux exigences des entreprises locales et internationales.

Conclusion

L'implémentation de la Clean Architecture dans un projet Full Stack Spring Boot et Angular est un investissement qui porte ses fruits à long terme. Elle garantit une meilleure séparation des préoccupations, une grande flexibilité face aux changements technologiques et une facilité accrue pour les tests et la maintenance.

Pour un expert Java Spring Boot Angular comme Laty Gueye Samba, Développeur Full Stack à Dakar, cette approche architecturale n'est pas seulement une bonne pratique, mais une nécessité pour développer des applications performantes et résilientes. En adoptant ces principes, les équipes peuvent bâtir des systèmes qui restent agiles et adaptables, même face à l'évolution rapide des besoins métier et des technologies.

Pour approfondir vos connaissances sur la Clean Architecture, il est recommandé de consulter les ressources officielles, notamment les écrits de Robert C. Martin, ainsi que la documentation et les bonnes pratiques des frameworks Spring et Angular.

À 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