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:
- Sécurité: ne pas exposer les champs sensibles (password)
- Performance: éviter le chargement lazy des relations
- Flexibilité: format différent de l'entité
- Découplage: changements internes n'affectent pas l'API
- 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:
| Annotation | Description |
|---|---|
@NotNull | Ne doit pas être null |
@NotBlank | Non null, non vide, non whitespace |
@NotEmpty | Non null et non vide |
@Size(min, max) | Taille entre min et max |
@Min, @Max | Valeur numérique min/max |
@Email | Format email valide |
@Pattern | Regex personnalisée |
@Past, @Future | Date 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:
- Controller (Présentation): gère HTTP, validation entrée
- Service (Métier): logique métier, transactions
- 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:
- Découplage: les clients dépendent de l'abstraction
- Testabilité: facile à mocker
- Flexibilité: changer l'implémentation
- 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:
| Code | Signification | Usage |
|---|---|---|
| 200 | OK | GET, PUT réussi |
| 201 | Created | POST réussi |
| 204 | No Content | DELETE réussi |
| 400 | Bad Request | Validation échouée |
| 401 | Unauthorized | Non authentifié |
| 403 | Forbidden | Non autorisé |
| 404 | Not Found | Ressource inexistante |
| 500 | Server Error | Erreur interne |
Score
| Score | Niveau |
|---|---|
| 27-30 | Excellent - Architecture maîtrisée |
| 21-26 | Bien - Quelques révisions |
| 15-20 | Moyen - Relisez les chapitres |
| Moins de 15 | À revoir - Reprenez les fondamentaux |