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.
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