Bien commencer le projet
Ce document est un tutoriel pour montrer comment bien démarrer le projet QuizMaker.
L’objectif est de comprendre le fonctionnement global du backend et les étapes nécessaires pour mettre en place la première route d’API : /api/user/login.
L’implémentation décrite peut varier selon les choix de chacun et évoluer au fil du développement.
Introduction
Ce projet est structuré en plusieurs modules Maven :
-
quizmaker-api: définit les objets de transfert (DTO) et les interfaces d’API. -
quizmaker-domain: contient les entités métiers, les interfaces de service et les repositories. -
quizmaker-spring: regroupe l’implémentation concrète, la configuration Spring Boot, Liquibase, et la génération des routes via OpenAPI.
Chaque module a un rôle précis et indépendant. Le but est d’obtenir un projet propre, structuré et facilement extensible.
Comprendre la logique du projet
Le flux complet d’une requête (par exemple un login) suit ce chemin :
-
L’utilisateur appelle une route définie dans OpenAPI (
openapi.yaml). -
OpenAPI génère automatiquement le contrat Java correspondant (
UserApi,LoginRequest, etc.). -
Le contrôleur Spring (
UserController) implémente cette interface et délègue la logique métier auUserService. -
Le service interroge les repositories (
StudentRepository,TeacherRepository) pour vérifier les informations. -
Si un utilisateur est trouvé, il renvoie un
UserDTO au client. -
Liquibase s’assure que les tables nécessaires existent dans la base de données.
Chaque couche a donc un rôle spécifique, sans dépendre directement des autres.
Étape 1 — Créer le modèle de données (module quizmaker-api)
Dans le module quizmaker-api, on définit un objet de transfert appelé User.
Cet objet représente les informations échangées entre le backend et le frontend :
-
un identifiant (
id) -
une adresse email (
email) -
un nom (
name) -
un prénom (
firstname) -
un rôle (
role), qui peut êtreSTUDENTouTEACHER.
|
Remplacez les |
Indication de code
public record User(Long id, String email, String name, String firstname, String role) {
}
Étape 2 — Définir le service métier (module quizmaker-domain)
Dans le module quizmaker-domain, on crée une interface UserService.
Cette interface décrit les opérations disponibles sur les utilisateurs, par exemple :
-
la connexion (
login) -
éventuellement l’inscription, la modification du profil, etc.
À ce stade, on ne fait qu’exposer une signature (pas d’implémentation). Cela permet de séparer la logique métier (implémentée plus tard) de la définition du contrat.
|
Remplacez les |
Indication de code
/**
* Authentifie un utilisateur à partir de son email et de son mot de passe.
* @param email email de l'utilisateur
* @param password mot de passe
* @return l'utilisateur authentifié (ou lève une exception en cas d'échec)
*/
User login(String email, String password);
Étape 3 — Créer les entités persistantes (module quizmaker-domain)
Toujours dans quizmaker-domain, on définit les entités JPA : PersistentStudent et PersistentTeacher.
Elles correspondent aux tables qui existeront dans la base de données.
Chaque entité contient :
-
un identifiant unique (
id) -
les champs
name,firstname,email, etpassword.
|
Remplacez les |
Indication de code pour l’entité PersistentStudent
public class PersistentStudent {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(nullable = false)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = 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;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFirstName(){
return firstname;
}
public void setFirstName(String firstname){
this.firstname = firstname;
}
public String getEmail(){
return email;
}
public void setEmail(String email){
this.email = email;
}
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
Indication de code pour l’entité PersistentTeacher
public class PersistentTeacher {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(nullable = false)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = 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;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFirstName(){
return firstname;
}
public void setFirstName(String firstname){
this.firstname = firstname;
}
public String getEmail(){
return email;
}
public void setEmail(String email){
this.email = email;
}
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
Étape 4 — Créer les repositories (module quizmaker-domain)
On ajoute deux repositories :
-
StudentRepository -
TeacherRepository
Ces interfaces héritent de JpaRepository et permettent à Spring Data JPA de générer automatiquement toutes les opérations de base (findAll, save, deleteById, etc.).
|
Ici, vous n’avez pas à les implémenter, ils sont déjà implémenter dans StudentRepository et TeacherRepository. |
Étape 5 — Implémenter la logique métier (module quizmaker-spring)
Dans le module quizmaker-spring, on implémente l’interface UserService dans une classe UserServiceImpl.
Cette classe contient la logique de connexion :
-
vérifier si l’adresse email et le mot de passe correspondent à un étudiant ou un enseignant,
-
renvoyer le DTO
Useravec le bon rôle (STUDENTouTEACHER), -
lever une exception si les identifiants sont invalides.
|
Remplacez les |
Indication de code pour UserServiceImpl
public User login(String email, String password) {
PersistentStudent s = studentRepository.findAll().stream()
.filter(u -> email != null && u.getEmail() != null && u.getEmail().equalsIgnoreCase(email))
.filter(u -> password != null && u.getPassword() != null && u.getPassword().equals(password))
.findFirst()
.orElse(null);
if (s != null) {
return new User(
s.getId(),
s.getEmail(),
s.getName(),
s.getFirstName(),
ROLE_STUDENT
);
}
PersistentTeacher t = teacherRepository.findAll().stream()
.filter(u -> email != null && u.getEmail() != null && u.getEmail().equalsIgnoreCase(email))
.filter(u -> password != null && u.getPassword() != null && u.getPassword().equals(password))
.findFirst()
.orElse(null);
if (t != null) {
return new User(
t.getId(),
t.getEmail(),
t.getName(),
t.getFirstName(),
ROLE_TEACHER
);
}
throw new IllegalArgumentException("Invalid email or password.");
}
Étape 6 — Définir la route API avec OpenAPI
OpenAPI permet de décrire les routes HTTP de manière standardisée.
Dans quizmaker-spring/src/main/resources/openapi/openapi.yaml, on définit les endpoints de notre API.
Pour la route /api/user/login :
-
on décrit qu’elle accepte une requête POST avec un
LoginRequestcontenantemailetpassword. -
on précise les réponses possibles (
200si succès,401si identifiants invalides). -
on relie la réponse au modèle
User.
Lors de la compilation du projet, le plugin OpenAPI Generator :
-
lit ce fichier YAML,
-
génère automatiquement les classes Java correspondantes (interface
UserApi, modèlesUseretLoginRequest), -
place ces fichiers dans
target/generated-sources/openapi/.
|
La route ici est déjà défini, nous vous laissons lire openapi.yaml. |
Étape 7 — Lier le contrôleur au service
Une fois la génération OpenAPI terminée, on crée un contrôleur UserController qui implémente UserApi.
Ce contrôleur :
-
reçoit la requête HTTP,
-
extrait les informations du
LoginRequest, -
appelle
userService.login(email, password), -
renvoie la réponse (
User) au client.
|
Remplacez les |
Indication de code pour UserController
public ResponseEntity<User> apiUserLoginPost(LoginRequest loginRequest) {
try {
var user = userService.login(loginRequest.getEmail(), loginRequest.getPassword());
User response = new User()
.id(user.id())
.email(user.email())
.name(user.name())
.firstname(user.firstname())
.role(user.role());
return ResponseEntity.ok(response);
} catch (IllegalArgumentException e) {
return ResponseEntity.status(401).build();
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}
Étape 8 — Gérer la base de données avec Liquibase
Liquibase est utilisé pour créer et versionner les tables en base.
Dans src/main/resources/db/changelog/db.changelog-master.xml, on définit les changements à appliquer (appelés changeSet).
Exemple :
-
création de la table
studentavec ses colonnes, -
création de la table
teacher.
Lors du démarrage de Spring Boot :
-
Liquibase détecte le changelog,
-
applique les modifications manquantes,
-
et maintient un historique des versions dans une table interne (
DATABASECHANGELOG).
|
Ici, nous vous invitons à lire le contenu de master.xml pour voir les deux tables présentes. |
Étape 9 — Comprendre le fonctionnement complet du login
Voici le flux complet d’une requête de connexion :
-
Le client envoie une requête POST vers
/api/user/loginavec l’email et le mot de passe. -
Spring Boot redirige cette requête vers la méthode correspondante du contrôleur généré via OpenAPI.
-
Le contrôleur appelle le service
UserService. -
Le service vérifie dans les repositories si un étudiant ou un enseignant correspond à l’email et au mot de passe.
-
Si un utilisateur est trouvé :
-
un
UserDTO est créé et renvoyé au contrôleur, -
qui le renvoie en réponse JSON au client avec un code HTTP
200.
-
-
Sinon, une exception est levée, et la réponse HTTP est
401 Unauthorized.
Chaque couche a donc une responsabilité claire :
-
OpenAPI → définit la structure de l’API.
-
Contrôleur → reçoit et renvoie les données.
-
Service → applique la logique métier.
-
Repository → interagit avec la base.
-
Liquibase → gère la structure de la base.
Étape 10 — Étendre le projet
Une fois la route /api/user/login fonctionnelle, tu peux facilement ajouter d’autres fonctionnalités :
-
créer de nouvelles routes OpenAPI (
/api/user/register,/api/quiz/create, etc.), -
ajouter de nouveaux
changeSetLiquibase pour les tables nécessaires, -
créer les DTO correspondants dans
quizmaker-api, -
implémenter les nouveaux services dans
quizmaker-spring.
Cette approche garantit que chaque ajout reste cohérent et documenté, sans duplication ni conflit entre modules.