Skip to Content
01 FundamentalsConcepts Micronaut

Concepts Micronaut

Guide des concepts fondamentaux de Micronaut Framework pour l’architecture hexagonale.

Micronaut est un framework moderne conçu pour les microservices avec AOT (Ahead-of-Time) compilation et faible empreinte mémoire.


IoC Container & Dependency Injection

Qu’est-ce que l’IoC (Inversion of Control) ?

Sans IoC - Couplage Fort

// ❌ Sans IoC - couplage fort public class TrendController { private GetTrendsUseCase useCase = new GetTrendsUseCase( new MockTrendRepositoryAdapter() ); // Problèmes : // - Impossible de changer l'adapter // - Difficile à tester // - Gestion manuelle du cycle de vie }

Avantages de l’IoC

L’IoC (Inversion of Control) délègue la création et la gestion des objets au framework.

  • Testabilité : facile de remplacer les dépendances par des mocks
  • Flexibilité : changer l’implémentation sans toucher au code
  • Découplage : les classes ne se connaissent que par leurs interfaces
  • Gestion du cycle de vie : le container crée et détruit les objets

Les Beans

Un bean est un objet géré par le container IoC de Micronaut.

Cycle de Vie d’un Bean

Le container Micronaut :

Crée l’instance

Au démarrage ou à la première utilisation

Injecte les dépendances

Automatiquement via le constructeur

Gère son cycle de vie

Appelle @PostConstruct après création

Le fournit partout

Disponible pour injection dans toute l’app


Comment Déclarer un Bean ?

Avec @Singleton

Pour les services, repositories, use cases :

@Singleton @RequiredArgsConstructor public class GetTrendsUseCase { private final TrendRepository repository; public Optional<TrendResult> execute(String keyword) { return repository.getTrends(new TrendQuery(keyword, "US")); } }

Caractéristiques :

  • ✅ Une seule instance dans toute l’application
  • ✅ Créée au démarrage (ou lazy à la première utilisation)
  • ✅ Partagée entre toutes les injections

Cas d’usage : Use cases, Services, Repositories (stateless)


Scopes de Beans

Le scope définit le cycle de vie d’un bean.

AnnotationCycle de vieUsage
@SingletonUne instance uniqueServices, repositories, use cases
@PrototypeNouvelle instance à chaque injectionObjets avec état
@RequestScopeUne instance par requête HTTPDonnées liées à la requête
@InfrastructureSingleton bas niveauConfigurations système

Injection de Dépendances

Constructor Injection (Recommandé)

@Singleton @RequiredArgsConstructor // Lombok génère le constructeur @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class MyService { TrendRepository repository; // ✅ Immutable, testable Logger logger; }

Avantages :

  • ✅ Champs immutables (final)
  • ✅ Facile à tester (on passe les dépendances au constructeur)
  • ✅ Visible dans la signature de la classe
  • ✅ Lombok + Micronaut travaillent ensemble

C’est la méthode recommandée dans toute l’architecture hexagonale !

Compilation AOT (Ahead-of-Time)

Différence avec Spring

FrameworkCompilationDémarrage
SpringReflection runtimeLent (scan classpath)
MicronautAOT (compile-time)Ultra rapide

Comment ça marche ?

Micronaut analyse vos annotations pendant la compilation :

mvn compile
  1. Micronaut scanne @Controller, @Singleton, etc.
  2. Génère du bytecode pour l’injection (pas de reflection)
  3. Crée les métadonnées dans target/classes/META-INF/

Résultat : Au runtime, pas de scan → démarrage instantané (< 1 seconde)

Vérifier la génération

# Voir les beans générés ls target/classes/META-INF/services/ cat target/classes/META-INF/services/io.micronaut.inject.BeanDefinitionReference

Micronaut + Lombok

CRITIQUE : L’ordre des annotation processors dans pom.xml est essentiel !

Le Piège de l’Ordre

Lombok DOIT être déclaré AVANT Micronaut dans le pom.xml :

<annotationProcessorPaths> <!-- ✅ 1. LOMBOK D'ABORD --> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.36</version> </path> <!-- ✅ 2. MICRONAUT APRÈS --> <path> <groupId>io.micronaut</groupId> <artifactId>micronaut-inject-java</artifactId> <version>${micronaut.version}</version> </path> </annotationProcessorPaths>

Pourquoi Cet Ordre ?

Phase 1 : Lombok génère le code

Lombok génère les constructeurs, getters, etc.

Phase 2 : Micronaut lit le bytecode

Micronaut voit le constructeur → crée l’injection automatiquement

Si Micronaut passe avant Lombok : il ne voit pas le constructeur généré → échec silencieux !

Combo Gagnant : @RequiredArgsConstructor

@Singleton @RequiredArgsConstructor // Lombok génère le constructeur @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class GetTrendsUseCase { TrendRepository repository; // Injecté par Micronaut Logger logger; }

Controllers REST

Créer un Controller

@Controller("/api/users") public class UserController { @Get // GET /api/users public List<User> list() { ... } @Get("/{id}") // GET /api/users/123 public User getById(Long id) { ... } @Post // POST /api/users public User create(@Body User user) { ... } @Put("/{id}") // PUT /api/users/123 public User update(Long id, @Body User user) { ... } @Delete("/{id}") // DELETE /api/users/123 public void delete(Long id) { ... } }

Paramètres de Requête

@QueryValue - Query String

@Get public String search( @QueryValue String keyword, // Obligatoire @QueryValue(defaultValue = "US") String region // Optionnel avec défaut ) { return "Searching for " + keyword + " in " + region; }

Requête : GET /api/trends?keyword=java&region=FR

Résultat :

  • keyword = "java"
  • region = "FR"

@PathVariable - Variable dans le Chemin

@Get("/{id}") public User getById(Long id) { // id vient du chemin }

Requête : GET /api/users/123 Résultat : id = 123

@Body - Corps de la Requête

@Post public User create(@Body User user) { // user contient les données JSON envoyées return userService.save(user); }

Requête :

POST /api/users Content-Type: application/json { "name": "Alice", "email": "alice@example.com" }

Résultat :

  • user.name = "Alice"
  • user.email = "alice@example.com"

Retour JSON Automatique

Micronaut convertit automatiquement les objets en JSON :

@Get public TrendResponseDto getTrends() { return new TrendResponseDto(...); // Devient du JSON automatiquement ! }

Résultat HTTP :

{ "keyword": "java", "region": "FR", "interestScore": 85 }

Validation

Micronaut valide automatiquement les paramètres :

@Controller("/api/trends") public class TrendController { @Get public TrendResponseDto getTrends( @QueryValue @NotBlank String keyword, @QueryValue @Pattern(regexp = "[A-Z]{2}") String region ) { // keyword et region sont validés automatiquement } }

Si la validation échoue, Micronaut retourne 400 Bad Request automatiquement.


Bonnes Pratiques

Suivez ces bonnes pratiques pour un code maintenable et testable.

Injection par Constructeur

// ✅ Recommandé @Singleton @RequiredArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class MyService { TrendRepository repository; }

Résumé

Micronaut + Lombok = Combo parfait pour l’architecture hexagonale !

ConceptExplication
BeanObjet géré par Micronaut
@SingletonBean unique dans l’app
@ControllerBean exposant des endpoints REST
IoCLe framework gère les dépendances
Constructor InjectionMeilleure pratique (avec Lombok)
AOTCompilation ultra rapide (vs Spring)
Lombok avant MicronautOrdre critique dans pom.xml

Prochaines Étapes

Maintenant que vous maîtrisez Micronaut, découvrez comment l’appliquer à l’architecture hexagonale.


Ressources

Last updated on