Architecture du back-end
Dans cette section, vous découvrirez l’architecture du back-end utilisée pour le projet transversal.
Principe général
Le back-end repose sur une architecture API REST. Cette approche permet une communication structurée entre le front-end et le back-end. Lorsque l’utilisateur interagit avec une fonctionnalité du site nécessitant un traitement côté serveur, une requête HTTP est envoyée vers une route spécifique, associée à un contrôleur.
Fonctionnement des contrôleurs
Un contrôleur agit comme un point d’entrée. Il est responsable de la réception de la requête, mais ne contient aucune logique métier.
|
Un contrôleur ne doit contenir aucun traitement métier. Son seul rôle est de déléguer le travail aux services appropriés. |
Rôle des services
Les services contiennent l’ensemble de la logique métier. Ils sont responsables de l’exécution des opérations demandées, comme l’accès aux données, la validation, ou la coordination entre plusieurs composants.
|
Cette séparation permet d’assurer un code modulaire, testable et maintenable. |
Principe de responsabilité unique
Chaque composant du back-end doit respecter le principe de responsabilité unique (Single Responsibility Principle). Cela signifie qu’un module (par exemple un service ou un contrôleur) doit avoir une seule responsabilité claire.
|
Le respect de ce principe améliore la lisibilité du code, facilite la maintenance, et limite les effets de bord. |
Organisation du projet
Dans le cadre du projet transversal, une architecture simple et pédagogique a été choisie. Elle permet de comprendre facilement les rôles de chaque composant tout en gardant l’implémentation accessible.
Voici la structure adoptée (multi-modules Maven) :
quizmaker-backend/
├── quizmaker-api/
│ └── src/main/java/fr/universite/nantes/quizmaker/common/dto/
│ └── ... # Objets de transfert (records Java)
│ └── pom.xml
├── quizmaker-domain/
│ └── src/main/java/fr/universite/nantes/quizmaker/data/
│ ├── entity/ # Entités JPA persistées
│ ├── repository/ # Interfaces d’accès aux données (Spring Data JPA)
│ └── service/ # Interfaces métier (contrats de service)
│ └── pom.xml
└── quizmaker-spring/
├── src/main/java/fr/universite/nantes/quizmaker/
│ ├── controller/ # Contrôleurs (points d’entrée de l’API REST)
│ ├── implService/ # Implémentations concrètes des services
│ └── QuizmakerApplication.java # Point d’entrée Spring Boot
└── src/main/resources/
├── db/changelog/ # Scripts Liquibase de migration de BDD
├── application.yml # Configuration centrale (BDD, Liquibase, Swagger, etc.)
└── openapi/
└── openapi.yaml # Spécification OpenAPI des routes à générer
└── pom.xml
|
Le dossier
|
|
Le fichier |
Description des répertoires principaux
controller/ (module quizmaker-spring)
Ce répertoire contient les contrôleurs REST, responsables de l’exposition des endpoints de l’API.
Dans le cadre de ce projet, les classes présentes ici implémentent les interfaces générées automatiquement par OpenAPI à partir du fichier openapi.yaml.
Chaque contrôleur correspond à un domaine fonctionnel (ex. : AuthApiController, UserApiController, etc.).
|
Les contrôleurs ne doivent contenir aucune logique métier. Ils se contentent de :
|
Exemple simplifié :
public class AuthApiController implements AuthApi {
private final AuthService authService;
public AuthApiController(AuthService authService) {
this.authService = authService;
}
@Override
public ResponseEntity<AuthResponse> login(LoginRequest request) {
return ResponseEntity.ok(authService.login(request));
}
}
service/ (module quizmaker-domain)
Ce dossier regroupe toutes les interfaces de service, c’est-à-dire les contrats métier proposés par chaque domaine (authentification, utilisateur, etc.).
Les interfaces définissent les fonctionnalités de haut niveau sans se préoccuper de leur implémentation.
|
Cela permet de :
|
Exemple :
public interface AuthService {
AuthResponse login(LoginRequest request);
}
implService/ (module quizmaker-spring)
Ce répertoire contient les implémentations concrètes des interfaces situées dans quizmaker-domain/service/.
C’est ici que réside toute la logique métier : vérifications, appels aux repositories, traitement de données, règles fonctionnelles, etc.
Chaque classe dans implService/ implémente une interface définie dans quizmaker-domain/service/.
|
La logique métier ne doit jamais être placée dans les contrôleurs ou les repositories. Elle doit toujours être centralisée ici, dans les services métier. |
Exemple :
public class AuthServiceImpl implements AuthService {
private final UserRepository userRepository;
public AuthServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public AuthResponse login(LoginRequest request) {
User user = userRepository.findByEmail(request.email())
.orElseThrow(() -> new UnauthorizedException("Utilisateur non trouvé"));
// Exemple simple : vérification brute du mot de passe (à sécuriser)
if (!user.getPassword().equals(request.password())) {
throw new UnauthorizedException("Mot de passe invalide");
}
return new AuthResponse("token-exemple");
}
}
repository/ (module quizmaker-domain)
Le répertoire repository/ contient les interfaces d’accès aux données.
Elles utilisent Spring Data JPA pour manipuler les entités en base de données sans avoir besoin d’implémenter manuellement les requêtes SQL.
Ces interfaces étendent JpaRepository et permettent d’accéder à des opérations courantes comme :
-
findById(…) -
findAll() -
save(…) -
deleteById(…)
|
Grâce à Spring Data, ces méthodes sont automatiquement générées à l’exécution, ce qui réduit considérablement le code nécessaire pour interagir avec la base. |
Une interface repository est liée à une entité.
On lui donne généralement un nom clair reflétant son usage, souvent basé sur le nom de l’entité manipulée (ex : UserRepository pour l’entité User).
Exemple :
@Repository
public interface UserRepository extends JpaRepository<User, Long> {}
|
Un repository ne contient aucune logique métier.
Il sert uniquement à interagir avec la base de données, et doit être utilisé exclusivement au sein des services métier ( |
entity/ (module quizmaker-domain)
Le répertoire entity/ contient les entités JPA représentant les tables de la base de données.
Chaque entité est une classe annotée avec @Entity et mappée à une table via @Table.
Ces entités définissent les champs qui seront persistés, ainsi que les métadonnées associées (nom de colonne, stratégie d’identifiant, etc.).
|
Dans ce projet, les entités peuvent être annotées avec |
Voici un exemple d’entité User :
import jakarta.persistence.*;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "user")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(nullable = false)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "firstname")
private String firstname;
@Column(name = "email")
private String email;
@Column(name = "password")
private String password;
// Getters & Setters
}
|
Les entités ne doivent jamais être utilisées dans les contrôleurs pour retourner des réponses API. Utilisez des DTO pour exposer les données nécessaires au client, et ainsi éviter les fuites de structure interne ou de données sensibles. |