Spring Boot microservices : architecture & déploiement

🏷️ Back-end 📅 06/01/2026 08:00:00 👤 Mezgani said
Springboot Microservices Java Devops Docker
Spring Boot microservices : architecture & déploiement

Concevez une architecture microservices robuste avec Spring Boot : REST controllers, Spring Data JPA, Docker, orchestration et communication inter-services.

Architecture microservices

Les microservices divisent une application monolithique en services indépendants. Spring Boot simplifie cette approche :

Principes clés : Chaque service est responsable d'un domaine métier unique, peut être déployé indépendamment et communique via des APIs.

Structure typique d'un projet Spring Boot microservices :

user-service/
├── src/main/java/com/app/
│   ├── UserApplication.java
│   ├── controller/
│   │   └── UserController.java
│   ├── service/
│   │   └── UserService.java
│   ├── repository/
│   │   └── UserRepository.java
│   └── model/
│       └── User.java
├── application.yml
└── pom.xml

Configuration Spring Boot pour microservices :

# application.yml
spring:
  application:
    name: user-service
  jpa:
    hibernate:
      ddl-auto: update
  datasource:
    url: jdbc:mysql://localhost:3306/users_db
    username: root
    password: password

server:
  port: 8081
  servlet:
    context-path: /api

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

REST Controllers

Les contrôleurs gèrent les requêtes HTTP et retournent du JSON :

// UserController.java
package com.app.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.app.model.User;
import com.app.service.UserService;
import java.util.List;

@RestController
@RequestMapping("/api/users")
@CrossOrigin("*")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        return ResponseEntity.ok(userService.findAll());
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return userService.findById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User created = userService.save(user);
        return new ResponseEntity<>(created, HttpStatus.CREATED);
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        user.setId(id);
        return ResponseEntity.ok(userService.save(user));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.deleteById(id);
        return ResponseEntity.noContent().build();
    }
}
Note : @CrossOrigin permet les requêtes cross-origin (CORS) depuis le frontend.

Spring Data JPA

Spring Data JPA fournit une abstraction pour l'accès aux données :

// User.java (Entity)
package com.app.model;

import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;

@Entity
@Table(name = "users")
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    private String name;

    @Column(name = "created_at")
    private LocalDateTime createdAt = LocalDateTime.now();
}

// UserRepository.java
package com.app.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.app.model.User;
import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
    List<User> findByNameContainingIgnoreCase(String name);
}

// UserService.java
package com.app.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.app.model.User;
import com.app.repository.UserRepository;
import java.util.List;
import java.util.Optional;

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public List<User> findAll() {
        return userRepository.findAll();
    }

    public Optional<User> findById(Long id) {
        return userRepository.findById(id);
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public void deleteById(Long id) {
        userRepository.deleteById(id);
    }
}

Conteneurisation avec Docker

Packagez vos services Spring Boot en conteneurs Docker :

# Dockerfile
FROM openjdk:17-jdk-slim

WORKDIR /app

COPY target/user-service-1.0.0.jar app.jar

EXPOSE 8081

ENTRYPOINT ["java", "-jar", "app.jar"]

Créez une pipeline de construction :

# pom.xml
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Build et run du conteneur :

# Builder l'image
mvn clean package
docker build -t user-service:1.0.0 .

# Lancer le conteneur
docker run -p 8081:8081 \
  -e SPRING_DATASOURCE_URL=jdbc:mysql://host.docker.internal:3306/users_db \
  user-service:1.0.0

Communication inter-services

Les microservices communiquent via HTTP ou asynchrone. Utilisez RestTemplate ou WebClient :

// OrderService - appel synchrone à UserService
package com.app.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private RestTemplate restTemplate;

    public void createOrder(Long userId, Order order) {
        // Vérifier que l'utilisateur existe
        String userUrl = "http://USER-SERVICE/api/users/" + userId;
        User user = restTemplate.getForObject(userUrl, User.class);

        if (user == null) {
            throw new RuntimeException("Utilisateur non trouvé");
        }

        // Créer la commande
        order.setUserId(userId);
        // Sauvegarder...
    }
}

// Configuration RestTemplate
@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Pour une communication asynchrone, utilisez RabbitMQ ou Kafka :

// RabbitMQ Producer (UserService)
@Component
public class UserEventPublisher {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void publishUserCreated(User user) {
        rabbitTemplate.convertAndSend("user-exchange", "user.created", user);
    }
}

// RabbitMQ Consumer (NotificationService)
@Component
public class UserEventListener {
    @RabbitListener(queues = "user-queue")
    public void onUserCreated(User user) {
        // Envoyer email de bienvenue
        sendWelcomeEmail(user.getEmail());
    }
}
À retenir : Les appels synchrones sont simples mais couplent les services. Préférez l'asynchrone pour la scalabilité.