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 :
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();
}
}
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());
}
}