Aller au contenu

Feature 04 — Chien (aligné sur le code réel Wouaf Wouaf)

Objectif : maîtriser une entité riche, avec relations, auto-références (père/mère), état, et exposition propre via DTO.


Pourquoi “Chien” est central ?

Dans Wouaf Wouaf, Chien est au cœur du diagramme :

C’est une entité parfaite pour aborder tous les aspects du mapping (ORM).


Ce que dit le code (attributs réels)

Dans Chien, on trouve :

Champ Type But
id Long identifiant technique
numeroTatouage String identifiant unique “terrain”
nom String nom usuel
endroitMarquage String localisation du tatouage
dateNaissance LocalDate âge / catégories / contrôles
couleurRobe String information descriptive
race Race lien vers référentiel
proprietaire Adherent propriétaire réel
pere Chien auto-référence (filiation)
mere Chien auto-référence (filiation)
etat EtatChien workflow métier

Et l’enum EtatChien peut contenir contenir : ENREGISTRE, INSCRIT_CONCOURS, EVALUATION, CONFIRME, NON_CONFIRME

À retenir : une entité réelle est plus riche qu’un exemple. Dans notre projet j’ai omis certaines informations comme le Pedigree.


UML (diagramme de classes)

Race 1 ---- * Chien
Adherent 1 ---- * Chien
Chien 0..1 ---- 0..* Chien (pere)
Chien 0..1 ---- 0..* Chien (mere)

Traduction :


JPA : points cruciaux

Unicité du tatouage

En base : numeroTatouage doit être unique, sinon, on ne sait plus identifier un chien.

Auto-références (père/mère)

Relation réflexive (Auto-référence) = relation vers la même entité.

Piège : boucles JSON si on expose l’entité directement.

Encore une raison d’utiliser DTO pour éviter la boucle infinie !


DTO existant (déjà dans le projet)

Le projet a ChienDto :

Ça permet :


MapStruct existant (dans le projet)

ChienMapper (MapStruct) fait la conversion Entity vers DTO.

Pourquoi c’est bien ?


Problème actuel et ce qu’on va améliorer

Dans notre contrôleur, on a (sauf si je l’ai déjà modifié) :

@GetMapping("/chiens")
public List<Chien> chiens() {
  return chienRepository.findAll();
}

Pourquoi c’est risqué ?

Refactor attendu : renvoyer List<ChienDto>.


Implémentation “propre” (controller dédié + DTO)

Endpoint de listing en mode allégé

@RestController
@RequestMapping("/api/chiens")
public class ChienController {

  private final ChienRepository repo;
  private final ChienMapper mapper;

  public ChienController(ChienRepository repo, ChienMapper mapper) {
    this.repo = repo;
    this.mapper = mapper;
  }

  @GetMapping
  public List<ChienDto> list() {
	// ici j'utilise la programmation fonctionnelle pour simplifier et renvoyer une version DTO
    return repo.findAll().stream().map(mapper::toDto).toList();
  }
}

Pourquoi c’est mieux ?


Ajouter la création (DTO d’entrée)

Le projet n’a pas forcément le DTO d’entrée : on le crée.

public record ChienCreateDto(
  @NotBlank String numeroTatouage,
  @NotBlank String nom,
  String endroitMarquage,
  LocalDate dateNaissance,
  String couleurRobe,
  @NotNull Long raceId,
  @NotNull Long proprietaireId,
  Long pereId,
  Long mereId
) {}

But :


Service de création (règles + chargement relations)

Pourquoi un service ?

Pseudo-code :

  1. vérifier tatouage unique
  2. charger race (sinon 404)
  3. charger adhérent (sinon 404)
  4. si pereId/mereId : charger chien parent
  5. set état = ENREGISTRE
  6. save

Tests (preuve)

Avec les Mocks.

Test listing doit retourner 200

@SpringBootTest
@AutoConfigureMockMvc
class ChienApiTest {

  @Autowired MockMvc mockMvc;

  @Test
  void list_chiens_returns_200() throws Exception {
    mockMvc.perform(get("/api/chiens")).andExpect(status().isOk());
  }
}

Test sécurité (plus tard)


A faire (si pas déjà fait)

  1. Renvoyer List<ChienDto>.
  2. Ajouter un endpoint GET /api/chiens/byTatouage/{num}.
  3. Ajouter un test intégration pour ce endpoint comme dans l’exemple.