Configuration du module métier

1. Structure

📁 quizmaker-backend/
 ├── 📁 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
 └── pom.xml

2. Configuration des paquets Java

Pour commencer, vous allez créer trois paquets Java qui vous aideront à organiser vos classes .

  1. Créez les paquet suivants à l’intérieur du dossier des sources du module quizmaker-domain :

    • fr.universite.nantes.quizmaker.data.entity

    • fr.universite.nantes.quizmaker.data.repository

    • fr.universite.nantes.quizmaker.data.service

3. Implémentation d’un service

Le paquet fr.universite.nantes.quizmaker.data.service 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 :

  • découpler la logique métier de sa mise en œuvre,

  • faciliter le test (mocking),

  • permettre plusieurs implémentations possibles si besoin.

À présent, vous allez créer l’interface nommée 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 service.

Voici un exemple de service :

public interface AuthService {
    AuthResponse login(LoginRequest request);
}
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);

4. Implémentation des entités

Le paquet fr.universite.nantes.quizmaker.data.entity contient les entités JPA représentant les tables de la base de données. Chaque entité est une classe annotée avec l’annotation @Entity et mappée à une table via l’annotation @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.).

Un exemple d’entité
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
}

Dans ce projet, les entités peuvent être annotées avec @Cache pour activer la mise en cache de second niveau avec Hibernate.

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.

  1. Implémentez, dans le paquet idoine, les entités JPA : PersistentStudent et PersistentTeacher.

Chaque entité doit contenir contient :

  • un identifiant unique (id)

  • les champs name, firstName, email, et password.

Indication de code pour l’entité PersistentStudent
@Entity
@Table(name = "student")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
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
@Entity
@Table(name = "teacher")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
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; }
}

5. Implémentation de référentiels

Le paquet fr.universite.nantes.quizmaker.data.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.

Chaque référentiel est lié à 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 (p.ex. : UserRepository pour l’entité User).

Exemple de référentiel
@Repository
public interface UserRepository extends JpaRepository<User, Long> {}
Un référentiel 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.
  1. Ajoutez deux référentiels au module :

    • StudentRepository

    • TeacherRepository

Ces interfaces doivent hériter de l’interface JpaRepository et permettent à Spring Data de générer automatiquement toutes les opérations de base (findAll, save, deleteById, etc.).