Aller au contenu principal

Quiz: Service Layer et Architecture

Testez vos connaissances sur la couche service, la validation et l'architecture MVC.

30 questions - Cliquez sur chaque question pour révéler la réponse.


Questions 1-10: Couche Service​

Question 1: Quel est le rôle de la couche Service dans l'architecture MVC?

Réponse: La couche Service contient la logique métier de l'application. Elle:

  • Orchestre les opérations entre contrôleurs et repositories
  • Applique les règles métier
  • Gère les transactions
  • Est indépendante du protocole HTTP

Question 2: Quelle annotation utilise-t-on pour déclarer un service Spring?

Réponse: @Service - C'est un stéréotype spécialisé de @Component qui indique que la classe contient de la logique métier.

@Service
public class UserService {
// logique métier
}

Question 3: Pourquoi ne pas mettre la logique métier dans le contrôleur?

Réponse: Séparer la logique métier du contrôleur permet:

  • Réutilisabilité: le service peut être appelé par plusieurs contrôleurs
  • Testabilité: tester la logique sans HTTP
  • Maintenabilité: code plus organisé
  • Séparation des responsabilités: chaque couche a un rôle précis

Question 4: Comment injecter un Repository dans un Service?

Réponse: Par injection de constructeur (recommandé):

@Service
public class UserService {

private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}

Question 5: Qu'est-ce qu'un DTO (Data Transfer Object)?

Réponse: Un DTO est un objet simple utilisé pour transférer des données entre les couches. Il:

  • Ne contient pas de logique métier
  • Expose uniquement les données nécessaires
  • Protège l'entité JPA de l'exposition directe
  • Permet de formater les données pour l'API

Question 6: Pourquoi utiliser des DTOs plutôt que les entités JPA directement?

Réponse:

  1. Sécurité: ne pas exposer les champs sensibles (password)
  2. Performance: éviter le chargement lazy des relations
  3. Flexibilité: format différent de l'entité
  4. Découplage: changements internes n'affectent pas l'API
  5. Validation: règles spécifiques à l'entrée/sortie

Question 7: Comment convertir une entité en DTO?

Réponse: Plusieurs approches:

// 1. Méthode manuelle
public UserDto toDto(User user) {
return new UserDto(user.getId(), user.getName(), user.getEmail());
}

// 2. Avec MapStruct
@Mapper
public interface UserMapper {
UserDto toDto(User user);
}

// 3. Avec ModelMapper
modelMapper.map(user, UserDto.class);

Question 8: Quelle est la différence entre un RequestDTO et un ResponseDTO?

Réponse:

  • RequestDTO: données entrantes (création, mise à jour), contient la validation
  • ResponseDTO: données sortantes, formatées pour le client
// Request
public class CreateUserRequest {
@NotBlank
private String email;
@Size(min = 8)
private String password;
}

// Response
public class UserResponse {
private Long id;
private String email;
// Pas de password!
}

Question 9: Comment gérer les erreurs dans la couche Service?

Réponse: Lever des exceptions personnalisées:

@Service
public class UserService {

public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("User not found: " + id));
}
}

Ces exceptions sont ensuite gérées par @ControllerAdvice.


Question 10: Qu'est-ce que @Transactional sur un service?

Réponse: @Transactional gère automatiquement les transactions:

  • Démarre une transaction au début de la méthode
  • Commit si succès
  • Rollback si exception
@Service
@Transactional
public class OrderService {
// Toutes les méthodes sont transactionnelles
}

Questions 11-20: Validation​

Question 11: Quelle dépendance ajouter pour la validation?

Réponse: spring-boot-starter-validation

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Question 12: Comment activer la validation sur un contrôleur?

Réponse: Avec @Valid ou @Validated sur le paramètre:

@PostMapping
public ResponseEntity<User> create(@Valid @RequestBody CreateUserRequest request) {
// Si validation échoue, MethodArgumentNotValidException est levée
}

Question 13: Quelles sont les annotations de validation courantes?

Réponse:

AnnotationDescription
@NotNullNe doit pas être null
@NotBlankNon null, non vide, non whitespace
@NotEmptyNon null et non vide
@Size(min, max)Taille entre min et max
@Min, @MaxValeur numérique min/max
@EmailFormat email valide
@PatternRegex personnalisée
@Past, @FutureDate dans le passé/futur

Question 14: Comment personnaliser le message d'erreur de validation?

Réponse: Avec l'attribut message:

public class CreateUserRequest {

@NotBlank(message = "L'email est obligatoire")
@Email(message = "Format email invalide")
private String email;

@Size(min = 8, message = "Le mot de passe doit contenir au moins 8 caractères")
private String password;
}

Question 15: Comment créer une validation personnalisée?

Réponse: Créer une annotation et un validateur:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueEmailValidator.class)
public @interface UniqueEmail {
String message() default "Email déjà utilisé";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
@Override
public boolean isValid(String email, ConstraintValidatorContext context) {
// Vérifier si email existe
}
}

Question 16: Quelle est la différence entre @Valid et @Validated?

Réponse:

  • @Valid: standard Java (JSR-380), cascade la validation
  • @Validated: Spring, supporte les groupes de validation
// Groupes de validation
public interface OnCreate {}
public interface OnUpdate {}

@NotNull(groups = OnUpdate.class)
private Long id;

@Validated(OnCreate.class)
public void create(...) { }

Question 17: Comment valider les paramètres de requête (query params)?

Réponse: Ajouter @Validated sur le contrôleur:

@RestController
@Validated
public class UserController {

@GetMapping("/users")
public List<User> search(
@RequestParam @Min(0) int page,
@RequestParam @Max(100) int size) {
// ...
}
}

Question 18: Comment valider un objet imbriqué?

Réponse: Utiliser @Valid sur le champ imbriqué:

public class OrderRequest {

@Valid // Cascade la validation
@NotNull
private AddressDto shippingAddress;
}

public class AddressDto {
@NotBlank
private String street;
@NotBlank
private String city;
}

Question 19: Comment récupérer les erreurs de validation?

Réponse: Via BindingResult ou @ExceptionHandler:

// Option 1: BindingResult
@PostMapping
public ResponseEntity<?> create(@Valid @RequestBody Request req, BindingResult result) {
if (result.hasErrors()) {
return ResponseEntity.badRequest().body(result.getAllErrors());
}
}

// Option 2: ControllerAdvice (recommandé)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(MethodArgumentNotValidException ex) {
// Extraire les erreurs
}

Question 20: Qu'est-ce que la validation au niveau service?

Réponse: Validation de la logique métier (pas seulement le format):

@Service
public class OrderService {

public Order createOrder(OrderRequest request) {
// Validation métier
if (request.getQuantity() > productService.getStock(request.getProductId())) {
throw new InsufficientStockException("Stock insuffisant");
}
// ...
}
}

Questions 21-30: Architecture​

Question 21: Quelles sont les 3 couches principales de l'architecture MVC?

Réponse:

  1. Controller (Présentation): gère HTTP, validation entrée
  2. Service (Métier): logique métier, transactions
  3. Repository (Données): accès base de données
Client → Controller → Service → Repository → Database

Question 22: Dans quelle couche placer la gestion des transactions?

Réponse: Dans la couche Service. C'est là que se trouve la logique métier qui définit ce qui constitue une unité de travail atomique.

@Service
@Transactional
public class TransferService {
public void transfer(Long fromId, Long toId, BigDecimal amount) {
// Toute l'opération est atomique
}
}

Question 23: Comment organiser les packages d'un projet Spring Boot?

Réponse: Par couche ou par fonctionnalité:

// Par couche
com.example.app
├── controller/
├── service/
├── repository/
├── model/
└── dto/

// Par fonctionnalité (recommandé pour grands projets)
com.example.app
├── user/
│ ├── UserController
│ ├── UserService
│ └── UserRepository
├── order/
└── product/

Question 24: Qu'est-ce que le principe de responsabilité unique (SRP)?

Réponse: Une classe ne doit avoir qu'une seule raison de changer. Chaque classe a une seule responsabilité:

  • Controller: gérer les requêtes HTTP
  • Service: logique métier
  • Repository: accès données

Mauvais exemple: un contrôleur qui fait tout (HTTP + métier + DB).


Question 25: Pourquoi utiliser des interfaces pour les services?

Réponse:

  1. Découplage: les clients dépendent de l'abstraction
  2. Testabilité: facile à mocker
  3. Flexibilité: changer l'implémentation
  4. Proxying: Spring AOP fonctionne mieux
public interface UserService {
User findById(Long id);
}

@Service
public class UserServiceImpl implements UserService { }

Question 26: Qu'est-ce qu'un @ControllerAdvice?

Réponse: Un composant qui gère les exceptions globalement pour tous les contrôleurs:

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(UserNotFoundException ex) {
return ResponseEntity.status(404).body(new ErrorResponse(ex.getMessage()));
}
}

Question 27: Comment structurer une réponse d'erreur standard?

Réponse: Créer un DTO d'erreur cohérent:

public class ErrorResponse {
private LocalDateTime timestamp;
private int status;
private String error;
private String message;
private String path;
}

// Ou format ProblemDetail (RFC 7807)

Question 28: Quelle est la différence entre @RestController et @Controller?

Réponse:

  • @Controller: retourne des vues (HTML)
  • @RestController: retourne des données (JSON/XML)

@RestController = @Controller + @ResponseBody

@RestController  // Tout est JSON
public class UserController { }

@Controller
public class ViewController {
@GetMapping("/")
public String home() {
return "index"; // Vue Thymeleaf
}
}

Question 29: Comment gérer les codes HTTP appropriés?

Réponse: Utiliser ResponseEntity:

@PostMapping
public ResponseEntity<User> create(@RequestBody UserRequest req) {
User user = userService.create(req);
return ResponseEntity
.status(HttpStatus.CREATED) // 201
.body(user);
}

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

Question 30: Quels sont les codes HTTP courants en REST?

Réponse:

CodeSignificationUsage
200OKGET, PUT réussi
201CreatedPOST réussi
204No ContentDELETE réussi
400Bad RequestValidation échouée
401UnauthorizedNon authentifié
403ForbiddenNon autorisé
404Not FoundRessource inexistante
500Server ErrorErreur interne

Score​

ScoreNiveau
27-30Excellent - Architecture maîtrisée
21-26Bien - Quelques révisions
15-20Moyen - Relisez les chapitres
Moins de 15À revoir - Reprenez les fondamentaux

Retour au Module 3