0%
Premiers pas avec Spring Boot

Premiers pas avec Spring Boot

Créer votre premier microservice

10-15 min

Premiers pas avec Spring Boot

Dans ce chapitre, nous allons créer notre premier microservice avec Spring Boot. Nous allons apprendre à configurer le projet, structurer le code et implémenter une API REST simple.

Configuration du projet

Création du projet avec Spring Initializr

  1. Dépendances essentielles

    <dependencies>
        <!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Spring Boot Starter Data JPA -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        
        <!-- H2 Database -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
  2. 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

Structure du projet

src/main/java/com/example/productservice/
├── ProductServiceApplication.java
├── controller/
│   └── ProductController.java
├── model/
│   └── Product.java
├── repository/
│   └── ProductRepository.java
├── service/
│   └── ProductService.java
└── dto/
    ├── ProductDTO.java
    └── CreateProductRequest.java

Implémentation

1. 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;
}

2. DTO (Data Transfer Objects)

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProductDTO {
    private Long id;
    private String name;
    private String description;
    private BigDecimal price;
    private Integer stock;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CreateProductRequest {
    private String name;
    private String description;
    private BigDecimal price;
    private Integer stock;
}

3. Repository

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

4. Service

@Service
@Slf4j
@RequiredArgsConstructor
public class ProductService {
    private final ProductRepository productRepository;
    
    public List<ProductDTO> getAllProducts() {
        return productRepository.findAll()
            .stream()
            .map(this::convertToDTO)
            .collect(Collectors.toList());
    }
    
    public ProductDTO getProductById(Long id) {
        return productRepository.findById(id)
            .map(this::convertToDTO)
            .orElseThrow(() -> new ProductNotFoundException(id));
    }
    
    public ProductDTO createProduct(CreateProductRequest request) {
        Product product = new Product();
        product.setName(request.getName());
        product.setDescription(request.getDescription());
        product.setPrice(request.getPrice());
        product.setStock(request.getStock());
        
        return convertToDTO(productRepository.save(product));
    }
    
    public ProductDTO updateProduct(Long id, CreateProductRequest request) {
        Product product = productRepository.findById(id)
            .orElseThrow(() -> new ProductNotFoundException(id));
            
        product.setName(request.getName());
        product.setDescription(request.getDescription());
        product.setPrice(request.getPrice());
        product.setStock(request.getStock());
        
        return convertToDTO(productRepository.save(product));
    }
    
    public void deleteProduct(Long id) {
        if (!productRepository.existsById(id)) {
            throw new ProductNotFoundException(id);
        }
        productRepository.deleteById(id);
    }
    
    private ProductDTO convertToDTO(Product product) {
        return new ProductDTO(
            product.getId(),
            product.getName(),
            product.getDescription(),
            product.getPrice(),
            product.getStock()
        );
    }
}

5. Controller

@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {
    private final ProductService productService;
    
    @GetMapping
    public ResponseEntity<List<ProductDTO>> getAllProducts() {
        return ResponseEntity.ok(productService.getAllProducts());
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<ProductDTO> getProductById(@PathVariable Long id) {
        return ResponseEntity.ok(productService.getProductById(id));
    }
    
    @PostMapping
    public ResponseEntity<ProductDTO> createProduct(@RequestBody CreateProductRequest request) {
        return ResponseEntity.status(HttpStatus.CREATED)
            .body(productService.createProduct(request));
    }
    
    @PutMapping("/{id}")
    public ResponseEntity<ProductDTO> updateProduct(
            @PathVariable Long id,
            @RequestBody CreateProductRequest request) {
        return ResponseEntity.ok(productService.updateProduct(id, request));
    }
    
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
        productService.deleteProduct(id);
        return ResponseEntity.noContent().build();
    }
}

6. Gestion des exceptions

@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(ProductNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleProductNotFoundException(ProductNotFoundException ex) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.NOT_FOUND.value(),
            ex.getMessage()
        );
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.INTERNAL_SERVER_ERROR.value(),
            "Une erreur inattendue s'est produite"
        );
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }
}

Tests

Test unitaire du service

@ExtendWith(MockitoExtension.class)
class ProductServiceTest {
    @Mock
    private ProductRepository productRepository;
    
    @InjectMocks
    private ProductService productService;
    
    @Test
    void createProduct_ShouldReturnProductDTO() {
        // Arrange
        CreateProductRequest request = new CreateProductRequest(
            "Test Product",
            "Description",
            new BigDecimal("10.00"),
            100
        );
        
        Product product = new Product(1L, "Test Product", "Description", 
            new BigDecimal("10.00"), 100);
            
        when(productRepository.save(any(Product.class))).thenReturn(product);
        
        // Act
        ProductDTO result = productService.createProduct(request);
        
        // Assert
        assertNotNull(result);
        assertEquals("Test Product", result.getName());
        assertEquals(new BigDecimal("10.00"), result.getPrice());
    }
}

Test d’intégration

@SpringBootTest
@AutoConfigureMockMvc
class ProductControllerIntegrationTest {
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    void createProduct_ShouldReturnCreatedProduct() throws Exception {
        // Arrange
        CreateProductRequest request = new CreateProductRequest(
            "Test Product",
            "Description",
            new BigDecimal("10.00"),
            100
        );
        
        // Act & Assert
        mockMvc.perform(post("/api/products")
                .contentType(MediaType.APPLICATION_JSON)
                .content(new ObjectMapper().writeValueAsString(request)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.name").value("Test Product"))
            .andExpect(jsonPath("$.price").value("10.00"));
    }
}

Exercices pratiques

  1. Création d’un service de commandes

    • Implémentez un service REST pour gérer les commandes
    • Ajoutez la validation des données
    • Implémentez la gestion des erreurs
  2. Ajout de fonctionnalités

    • Implémentez la pagination des résultats
    • Ajoutez le tri des produits
    • Implémentez la recherche par critères
  3. Tests

    • Ajoutez des tests unitaires pour le service
    • Implémentez des tests d’intégration
    • Ajoutez des tests de performance

Conclusion

Dans ce chapitre, nous avons créé notre premier microservice avec Spring Boot. Nous avons appris à :

  • Configurer un projet Spring Boot
  • Structurer le code selon les bonnes pratiques
  • Implémenter une API REST
  • Gérer les exceptions
  • Écrire des tests

Dans le prochain chapitre, nous explorerons les différentes stratégies de communication entre microservices.

Commentaires

Les commentaires sont alimentés par GitHub Discussions

Connectez-vous avec GitHub pour participer à la discussion

Lien copié !