GLO-4002 2026

DDD : Agrégats et repositories

Conception des agrégats

Nous avons vu 3 principaux éléments de la conception dirigée par le domaine.

  1. la notion de ubiquitous language : faire en sorte que les concepts et le vocabulaire du domaine se retrouvent dans notre code
  2. les agrégats : une technique pour regrouper des objets afin de garantir la consistance du domaine
  3. les value objects et les entities : deux types d'objets qui ont une notion d'identité différente

Un des sujets qu'il est facile de mal comprendre est la conception des agrégats et leur relation avec les repositories.

Exercice : retour sur les concepts évoqués jusqu'à maintenant

Si nous retournons à l'atelier sur la gestion des patients, nous avons

  • une tête d'agrégat avec la classe PatientAssurable
  • PatientAssurable contient un HistoriquePatient
  • HistoriquePatient contient une liste de SejourHorsQuebec

Quelle notion du DDD a mené au nommage de ces classes?

☑️ Solution

Il s'agit du ubiquitous language. Les notions PatientAssurable, HistoriquePatient et SejourHorsQuebec font partie du domaine et sont utilisées par les personnes de la RAMQ qui traitent les dossiers d'assurabilité. C'est pourquoi on les retrouve dans notre code.

Parmi ces 3 classes, lesquelles seraient des value object?

  1. PatientAssurable
  2. HistoriquePatient
  3. SejourHorsQuebec
☑️ Solution

Réponse B et C :

  • Un historique est purement défini par ses attributs (date de naissance, date d'inscription, date de décès)
  • Un séjour est purement défini par ses attributs (date de départ, durée et motif)

À l'inverse, un PatientAssurable peut évoluer dans le temps tout en possédant une identité propre.

De ce fait, quels problèmes voyez-vous dans la conception des objets?

☑️ Solution
  • PatientAssurable est une entité et devrait donc pouvoir être identifiée sans se baser sur ses attributs. Il lui faudrait un champ d'identification unique
  • HistoriquePatient est un value object et devrait donc être immuable. Les méthodes ajouterSejour, ajouterDeces, ajouterInscriptionRAMQ ne devraient pas changer l'instance à laquelle elles sont attachées. Il faudrait plutôt recréer une nouvelle instance lorsqu'on veut un nouvel historique.

Le rôle principal des agrégats est de garantir le respect des règles du domaine et notamment des invariants. Pouvez-vous identifier les différents invariants garantis par l'agrégat PatientAssurable ?

☑️ Solution
  • Il est invalide d'avoir une date de décès avant la date de naissance
  • Il est invalide d'avoir un séjour avant sa naissance ou après son décès
  • Il est invalide d'avoir 2 séjours différents avec des dates qui se chevauchent
  • Il est invalide de s'inscrire avant sa naissance ou après son décès
  • Un patient n'est pas assuré avant sa naissance ou après son décès
  • Un patient doit être inscrit pour être assuré
  • Un patient qui quitte le Québec pour plus de 183 jours n'est plus assuré sauf s'il quitte pour travailler ou étudier

Exercice : agrégats et repository

L'Université Laval se munit d'un forum de discussion similaire à Reddit. Ce forum est organisé en sous-forums (annonces générales, vie étudiante, génie informatique, médecine, etc.). Les utilisateurs peuvent poster des messages dans les sous-forums, "upvoter" ces messages et/ou y répondre, ils peuvent également s'abonner à des sous-forums pour avoir les nouvelles dans leur fil d'actualité.

Nous allons modéliser ce domaine avec 2 agrégats :

  • les utilisateurs
  • le forum, qui contient les sous-forums, les messages et les réponses

Veuillez cloner le repository github

Agrégat Utilisateur

Commençons par étudier le code se rapportant aux utilisateurs

  • Dans le package domaine.utilisateurs nous avons un agrégat. Utilisateur (identifié par son courriel) est la tête d'agrégat. Courriel est un value object.
  • Dans le package persistence.accesdonnees nous avons UtilisateurJSON qui représente la structure de données d'un utilisateur au format JSON et UtilisateurJSONDAO pour lire et écrire des utilisateurs dans un fichier.

Votre premier exercice consiste à créer une interface UtilisateurRepository ainsi qu'une implémentation de cette interface qui utilisera la DAO et les structures de données pour lire et écrire dans un fichier. Pour cela, il faut faire compiler puis passer les tests dans la classe _exercice1.UtilisateurRepositoryJSONTest.

Conseils :

  • Assurez-vous de créer les bonnes classes et interfaces aux bons endroits.
  • Faites passer les tests un à la fois
  • Une fois tous les tests unitaires au vert, vous pouvez vérifier vos résultats avec un scénario d'ensemble dans _exercice1.UtilisateurRepositoryJSONVerificationComplete
  • Ne modifiez pas le code fourni, vous devez seulement créer du nouveau code qui utilise l'existant.
  • Ne vous préoccupez pas trop de la gestion d'erreurs et de fonctionnalités avancées. Le but est de bien comprendre le lien entre agrégats et repository ainsi que de placer les interfaces et implémentations aux bons endroits. Nous n'essayons pas de faire un repository parfait.
☑️ Solution

Liste de vérification

  • Est-ce que votre interface UtilisateurRepository se trouve bien dans le package domain (normalement dans domain.utilisateurs)?
  • Est-ce que votre implémentation UtilisateurRepositoryJSON se trouve bien dans le package persistence? Est-ce que le UtilisateurJSONDAO est utilisé par le UtilisateurRepositoryJSON pour ses opérations?
  • N'y a-t-il bien aucune dépendance de domain vers persistence?
  • Tous les tests de _exercice1 sont au vert?

Agrégat Forum

Passons maintenant à l'agrégat Forum qui contient des Messages organisés en SousForums et qui peuvent avoir des Reponses associées. De la même façon que pour Utilisateur, vous avez du code de DAO et des structures de données JSON qui sont fournies.

Avant de vous lancer dans la création des repository, étudiez bien le code du domaine. Une erreur s'est glissée dans la conception des agrégats, pouvez-vous la repérer?

☑️ Solution

Comparez le champ auteur de Message et de Reponse.

Pour l'un, nous avons un value-object Courriel, l'autre référence l'agrégat Utilisateur directement. Il est très rare et peu recommandé qu'un agrégat référence un autre agrégat. Message et Reponse doivent utiliser Courriel tous les deux.

Procédez à la correction nécessaire. Le compilateur vous guidera vers d'autres erreurs à corriger ce qui ne devrait pas vous prendre trop de temps. Une fois cette correction faite, vous pouvez commencer à implémenter l'interface ForumRepository en créant un Repository capable d'interagir avec les DAO JSON fournies. Les tests dans _exercice2.ForumRepositoryJSONTest sont là pour vous guider. Encore une fois, il n'est pas question d'implémenter un Repository parfait avec de nombreuses fonctionnalités et une gestion d'erreur en béton. Concentrez-vous sur

  • Comprendre qu'un Repository est responsable de la persistance d'un agrégat correspondant (dans notre cas, simple lecture et écriture)
  • Faire passer les tests, une étape à la fois en écrivant du code propre. Souvenez-vous des ateliers de réusinage : les principes OO pourront vous aider à maintenir votre code lisible et maintenable.
  • Respecter le sens des dépendances (l'interface vs l'implémentation du repository, dans quelle couche etc.)
☑️ Solution

Liste de vérification

  • Est-ce que votre interface ForumRepository se trouve bien dans le package domain (normalement dans domain.messages)?
  • Est-ce que votre implémentation ForumRepositoryJSON se trouve bien dans le package persistence? Est-ce qu'elle utilise les DAO pour les SousForum, les Message et les Reponse?
  • N'y a-t-il bien aucune dépendance de domain vers persistence?
  • Tous les tests de _exercice2.ForumRepositoryJSONTest sont au vert?

Pour aller plus loin

Ce document de référence de Vaughn Vernon offre une bonne vue d'ensemble sur la conception des agrégats. Il est fortement recommandé de le lire.