Formation Angular moderne : signals, NgRx et tests

Angular router, services et HttpClient vers un backend Spring Boot

Développez une architecture Angular modulaire, propre et scalable. Connectez l’application à un backend Spring Boot en appliquant les patterns professionnels.

Leçon gratuite

Objectif du Jour 2

Un service Angular avec HttpClient relie l’application à son backend, ici une API Spring Boot. Aujourd’hui, on passe au niveau supérieur : structure modulaire, services orientés métier et connexion propre à une API Spring Boot. Vous allez apprendre à construire une application scalable, testable et facile à maintenir.

🎯 Ce que vous allez maîtriser

  • Modularité Angular (feature modules, standalone features, lazy loading)
  • Services métier orientés patterns (Mediator, Facade, Repository Client)
  • Connexion backend Spring Boot propre et sécurisée
  • Meilleures stratégies d’optimisation et d’organisation du code

🔧 Fil Rouge

Notre application B2B : achats, catalogue, connexion, commandes.

On structure les features, on isole les services, et on branche l’API Spring Boot.

1 — Modularité Professionnelle Angular

Une application Angular mal structurée devient rapidement incontrôlable. La modularité permet une architecture claire, scalable, testable.

🌐 Feature Modules — Organisation standard entreprise

  • catalog/ — produits, filtres, détails
  • auth/ — login, token, guards
  • cart/ — panier, récap, validation

Les modules permettent :

  • isolement logique
  • lazy loading → performances
  • réutilisation (shared modules)

💡 Sans modularité vs Avec modularité

❌ Sans : fichiers éparpillés, importations croisées, dette technique.

✔ Avec :

  • navigation claire
  • performance optimisée
  • préparation au microfrontend

📦 Exemple de module propre

// Approche historique (NgModule) — encore présente dans beaucoup de projets
@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild(routes)
  ],
  declarations: [
    ProductListComponent,
    ProductDetailComponent
  ]
})
export class CatalogModule { }

// Approche moderne (Angular 15+, standard en 17+) : pas de NgModule, des routes lazy
// app.routes.ts
export const routes: Routes = [
  { path: 'catalog', loadChildren: () => import('./catalog/catalog.routes').then(m => m.CATALOG_ROUTES) }
];
// catalog/catalog.routes.ts
export const CATALOG_ROUTES: Routes = [
  { path: '', loadComponent: () => import('./product-list.component').then(m => m.ProductListComponent) },
  { path: ':id', loadComponent: () => import('./product-detail.component').then(m => m.ProductDetailComponent) }
];

Astuce performance

Chargez les pages rarement consultées en lazy loading uniquement. Gain : -25% de JS au premier chargement.

2 — Services & Injection de Dépendances

Les services Angular sont le cœur de votre logique métier. Ils doivent être simples, isolés et testables.

⚙ Patterns appliqués

  • Mediator — Orchestration de plusieurs API/services
  • Proxy — Intercepter et transformer les appels HTTP
  • Client Repository — Accès API structuré

Gains :

  • Lisibilité ×2
  • Test unitaire ×4 plus simple
  • Aucune duplication de logique

🔧 Exemple de service propre

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Product } from './product.model';

@Injectable({ providedIn: 'root' })
export class ProductService {
  private readonly http = inject(HttpClient);

  getAll(): Observable<Product[]> {
    return this.http.get<Product[]>('/api/products'); // réponse typée
  }
}

À éviter

  • Business logic dans les composants ❌
  • Services « dieu » qui font tout ❌
  • HTTP dans 10 fichiers différents ❌

3 — Connexion Pro à un Backend Spring Boot

Le backend Spring Boot fournit l’API REST. Angular consomme les endpoints propres via HttpClient et les patterns réactifs.

💡 Structure Spring Boot

  • Controller — Endpoints REST
  • Service — Logique métier
  • Repository — Accès JPA
@RestController
@RequestMapping("/api/products")
public class ProductController {

    private final ProductService service;

    public ProductController(ProductService service) {
        this.service = service;
    }

    @GetMapping
    public List<ProductResponse> findAll() { // type générique explicite
        return service.findAll();
    }
}

🔗 Connexion Angular

// Option 1 : désabonnement automatique à la destruction du composant
this.productService.getAll()
  .pipe(takeUntilDestroyed(this.destroyRef))
  .subscribe(products => {
    this.products = products;
  });

// Option 2 (recommandée) : exposer directement un signal, sans subscribe
readonly products = toSignal(this.productService.getAll(), { initialValue: [] });

Astuce : Utilisez tap() pour debugger sans polluer.

Gain

Architecture claire compatible DDD + microservices.

Astuce d’expert

En développement, ne codez jamais http://localhost:8080 dans Angular : déclarez un proxy.conf.json ("/api": { "target": "http://localhost:8080" }) référencé dans angular.json. Vos appels restent relatifs (/api/products), sans CORS à configurer, et l’URL de production ne change pas.

4 — Signals pour gérer l’état proprement

Signals est l’approche moderne pour gérer l’état dans Angular. Plus performant, plus simple, plus prédictif que Subject/BehaviorSubject.

readonly products = signal<Product[]>([]); // typé : sinon never[] en TS strict
readonly promos = computed(
  () => this.products().filter(p => p.price < 100)
);
// dans un contexte d'injection (initialiseur de champ ou constructeur)
private readonly logPromos = effect(() => console.log("Promos:", this.promos()));

Gains

  • Réactivité automatique (pas besoin de souscriptions)
  • Debug ×2 plus simple
  • Perf meilleure pour 90% des cas

Suivez votre progression. Créez un compte gratuit pour cocher vos leçons et reprendre là où vous en étiez.

Créer un compte gratuit