0%
Spring Boot pour les Microservices

Spring Boot pour les microservices

Premier microservice

10-15 min

Spring Boot pour les Microservices

Dans ce tutoriel, nous allons créer notre premier microservice avec Spring Boot. Nous allons voir comment configurer le projet, implémenter les fonctionnalités de base et préparer le service pour une architecture microservices.

Configuration du projet

Prérequis

  • Java 17 ou supérieur
  • Maven ou Gradle
  • IDE (IntelliJ IDEA, Eclipse, VS Code)
  • Git

Création du projet

  1. Utilisation de Spring Initializr

    Visitez start.spring.io et configurez votre projet :

    • Project: Maven
    • Language: Java
    • Spring Boot: 3.2.x
    • Java: 17
    • Dependencies:
      • Spring Web
      • Spring Data JPA
      • H2 Database
      • Lombok
      • Spring Boot DevTools
  2. Structure du projet

    src/
    ├── main/
    │   ├── java/
    │   │   └── com/example/
    │   │       └── microservice/
    │   │           ├── MicroserviceApplication.java
    │   │           ├── controller/
    │   │           ├── service/
    │   │           ├── repository/
    │   │           ├── model/
    │   │           └── config/
    │   └── resources/
    │       └── application.yml
    └── test/
        └── java/
            └── com/example/
                └── microservice/

Configuration de base

application.yml

server:
  port: 8080

spring:
  application:
    name: product-service
  datasource:
    url: jdbc:h2:mem:productdb
    driver-class-name: org.h2.Driver
    username: sa
    password: 
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: update
  h2:
    console:
      enabled: true
      path: /h2-console

Configuration de base

@SpringBootApplication
public class MicroserviceApplication {
    public static void main(String[] args) {
        SpringApplication.run(MicroserviceApplication.class, args);
    }
}

Implémentation du microservice

Modèle de données

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    private String description;
    private BigDecimal price;
    private Integer stock;
}

Repository

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findByPriceLessThan(BigDecimal price);
}

Service

@Service
@Slf4j
public class ProductService {
    private final ProductRepository productRepository;

    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    public List<Product> getAllProducts() {
        return productRepository.findAll();
    }

    public Product getProductById(Long id) {
        return productRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("Product not found"));
    }

    public Product createProduct(Product product) {
        return productRepository.save(product);
    }

    public Product updateProduct(Long id, Product product) {
        Product existingProduct = getProductById(id);
        existingProduct.setName(product.getName());
        existingProduct.setDescription(product.getDescription());
        existingProduct.setPrice(product.getPrice());
        existingProduct.setStock(product.getStock());
        return productRepository.save(existingProduct);
    }

    public void deleteProduct(Long id) {
        productRepository.deleteById(id);
    }
}

Controller

@RestController
@RequestMapping("/api/products")
@Slf4j
public class ProductController {
    private final ProductService productService;

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

    @GetMapping
    public ResponseEntity<List<Product>> getAllProducts() {
        return ResponseEntity.ok(productService.getAllProducts());
    }

    @GetMapping("/{id}")
    public ResponseEntity<Product> getProductById(@PathVariable Long id) {
        return ResponseEntity.ok(productService.getProductById(id));
    }

    @PostMapping
    public ResponseEntity<Product> createProduct(@RequestBody Product product) {
        return ResponseEntity.status(HttpStatus.CREATED)
            .body(productService.createProduct(product));
    }

    @PutMapping("/{id}")
    public ResponseEntity<Product> updateProduct(
            @PathVariable Long id,
            @RequestBody Product product) {
        return ResponseEntity.ok(productService.updateProduct(id, product));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
        productService.deleteProduct(id);
        return ResponseEntity.noContent().build();
    }
}

Tests

Test unitaire du service

@SpringBootTest
class ProductServiceTest {
    @Autowired
    private ProductService productService;

    @MockBean
    private ProductRepository productRepository;

    @Test
    void getAllProducts_ShouldReturnAllProducts() {
        // Arrange
        List<Product> expectedProducts = Arrays.asList(
            new Product(1L, "Product 1", "Description 1", new BigDecimal("10.00"), 100),
            new Product(2L, "Product 2", "Description 2", new BigDecimal("20.00"), 200)
        );
        when(productRepository.findAll()).thenReturn(expectedProducts);

        // Act
        List<Product> actualProducts = productService.getAllProducts();

        // Assert
        assertEquals(expectedProducts, actualProducts);
    }
}

Test d’intégration

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ProductControllerIntegrationTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void createProduct_ShouldReturnCreatedProduct() {
        // Arrange
        Product product = new Product(null, "Test Product", "Test Description", 
            new BigDecimal("10.00"), 100);

        // Act
        ResponseEntity<Product> response = restTemplate.postForEntity(
            "/api/products", product, Product.class);

        // Assert
        assertEquals(HttpStatus.CREATED, response.getStatusCode());
        assertNotNull(response.getBody());
        assertEquals("Test Product", response.getBody().getName());
    }
}

Préparation pour les microservices

Configuration de la santé du service

@Configuration
public class HealthConfig {
    @Bean
    public HealthIndicator productServiceHealthIndicator() {
        return new HealthIndicator() {
            @Override
            public Health health() {
                return Health.up()
                    .withDetail("service", "product-service")
                    .withDetail("status", "running")
                    .build();
            }
        };
    }
}

Configuration des métriques

management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus
  endpoint:
    health:
      show-details: always

Déploiement

Dockerfile

FROM eclipse-temurin:17-jdk-alpine
VOLUME /tmp
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

docker-compose.yml

version: '3.8'
services:
  product-service:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - SPRING_DATASOURCE_URL=jdbc:h2:mem:productdb

Conclusion

Nous avons créé un microservice de base avec Spring Boot qui :

  • Expose une API REST pour la gestion des produits
  • Utilise une base de données H2 en mémoire
  • Inclut des tests unitaires et d’intégration
  • Est prêt pour le déploiement avec Docker

Dans le prochain tutoriel, nous allons implémenter la découverte de services avec Eureka pour permettre à nos microservices de se trouver et communiquer entre eux.

Commentaires

Les commentaires sont alimentés par GitHub Discussions

Connectez-vous avec GitHub pour participer à la discussion

Lien copié !