Données et domaine¶
Structurer ce que le système manipule — classes, relations, entités, et les règles qui gouvernent leur cohérence.
Pourquoi modéliser les données¶
Les données sont au cœur de tout système d'information. Sans modèle de données, on decouvre les incoherences au moment de l'implémentation : des attributs manquants, des relations ambigues, des doublons semantiques. Un champ "adresse" dans une table peut représenter l'adresse de facturation, l'adresse de livraison ou les deux — et cette ambiguite se paie en bugs, en code defensif et en migrations douloureuses.
Modéliser les données, c'est rendre explicite la structure de ce que le système manipule. On distingue deux niveaux de modélisation :
- Le modèle de domaine : la représentation métier, indépendante de toute technologie de stockage.
- Le modèle de persistance : la traduction du domaine vers une base de données relationnelle, documentaire ou autre.
Les confondre est l'erreur la plus repandue. Le modèle de domaine capture la semantique métier. Le modèle de persistance capture les contraintes techniques. On commence toujours par le domaine.
Diagramme de classes UML¶
Le diagramme de classes est l'outil central pour modéliser le domaine. Il représenté les entités, leurs attributs, leurs opérations et leurs relations.
Anatomie d'une classe¶
Une classe se decompose en trois compartiments :
- Nom : le concept métier (PascalCase par convention)
- Attributs : les données que l'entité porte, avec type et visibilité
- Méthodes : les comportements de l'entité (optionnel en phase d'analyse)
Visibilité¶
| Symbole | Visibilité | Description |
|---|---|---|
| + | Public | Accessible par tous |
| - | Prive | Accessible uniquement depuis la classe |
| # | Protégé | Accessible depuis la classe et ses filles |
| ~ | Package | Accessible depuis le même package |
Exemple : modèle de domaine e-commerce¶
classDiagram
class Client {
-id : UUID
-nom : String
-email : String
-dateInscription : Date
+passerCommande() Commande
+consulterHistorique() List~Commande~
}
class Commande {
-id : UUID
-dateCreation : Date
-statut : StatutCommande
-montantTotal : Decimal
+ajouterLigne(produit, quantite)
+valider()
+annuler()
}
class LigneCommande {
-quantite : Integer
-prixUnitaire : Decimal
+sousTotal() Decimal
}
class Produit {
-id : UUID
-nom : String
-description : String
-prixCatalogue : Decimal
-stock : Integer
+estDisponible() Boolean
}
class Adresse {
-rue : String
-codePostal : String
-ville : String
-pays : String
}
Client "1" --> "0..*" Commande : passe
Client "1" --> "1..*" Adresse : possede
Commande "1" *-- "1..*" LigneCommande : contient
LigneCommande "0..*" --> "1" Produit : reference
Commande "1" --> "1" Adresse : livraison Les relations¶
Les relations entre classes expriment comment les entités du domaine interagissent. Chaque type de relation a une semantique précisé.
Types de relations¶
| Relation | Notation UML | Semantique | Exemple |
|---|---|---|---|
| Association | Trait plein | Lien structurel entre deux classes | Client — Commande |
| Aggregation | Losange vide | Le tout contient des parties (vie indépendante) | Équipe — Employe |
| Composition | Losange plein | Le tout possédé des parties (vie liee) | Commande — LigneCommande |
| Héritage | Fleche triangle | Specialisation / generalisation | ComptePro hérité de Compte |
| Dépendance | Fleche pointillee | Utilisation temporaire | Service — Logger |
| Realisation | Fleche pointillee triangle | Implémentation d'une interface | PaymentService — Payable |
Multiplicites¶
Les multiplicites precisent combien d'instances participent à la relation.
| Notation | Signification |
|---|---|
| 1 | Exactement un |
| 0..1 | Zero ou un (optionnel) |
| 0..* | Zero ou plusieurs |
| 1..* | Un ou plusieurs |
| n..m | Entre n et m |
Aggregation vs composition
La question clé : si le conteneur est détruit, les éléments survivent-ils ? Si oui, c'est une aggregation (un employe survit à la dissolution d'une équipe). Si non, c'est une composition (une ligne de commande n'a pas de sens sans sa commande). Dans le doute, on utilise une association simple — l'aggregation et la composition sont des precisions, pas des obligations.
Héritage vs composition¶
L'héritage ("est-un") est souvent surutilise en modélisation. Avant de créer une hiérarchie de classes, on se pose la question : la relation est-elle vraiment un "est-un" au sens métier ? Un CompteProfessionnel est-il un Compte, ou un Compte avec une configuration différente ?
La règle du Liskov Substitution Principle s'applique : partout ou on utilise la classe parent, on doit pouvoir utiliser la classe fille sans casser le comportement. Si ce n'est pas le cas, l'héritage est mal utilisé et la composition est preferable.
Modèle entité-relation¶
Le modèle entité-relation (ER) est l'outil classique pour passer du domaine à la base de données. Il existe trois niveaux de détail.
MCD — Modèle Conceptuel de Données¶
Le MCD capture les entités et leurs associations au niveau métier, sans aucune consideration technique. On utilise la notation de Chen ou la notation Merise (en France). Les relations sont nommees et portent des cardinalites.
MLD — Modèle Logique de Données¶
Le MLD traduit le MCD en un schéma relationnel : les entités deviennent des tables, les associations deviennent des clés etrangeres ou des tables de jointure, les attributs deviennent des colonnes typees.
MPD — Modèle Physique de Données¶
Le MPD ajoute les détails spécifiques au SGBD cible : index, partitionnement, types natifs, contraintes de performance.
| Niveau | Question | Audience | Contenu |
|---|---|---|---|
| MCD | Quoi ? | Métier, analyste | Entités, associations, cardinalites |
| MLD | Comment logiquement ? | Concepteur | Tables, colonnes, clés, jointures |
| MPD | Comment physiquement ? | DBA, ops | Index, partitions, types SGBD |
graph LR
MCD[MCD - Conceptuel] --"traduction"--> MLD[MLD - Logique]
MLD --"implementation"--> MPD[MPD - Physique]
MPD --"deploiement"--> DB[(Base de donnees)] Modèle de domaine vs modèle de persistance¶
C'est une distinction que beaucoup de projets ignorent — au prix d'un couplage fort entre la logique métier et le stockage.
Le problème¶
Quand le modèle de domaine et le schéma de base de données sont identiques (ORM avec mapping direct), toute modification de schéma impacte la logique métier et vice versa. On ne peut pas optimiser le stockage sans toucher au domaine. On ne peut pas enrichir le domaine sans migrer la base.
La séparation¶
| Aspect | Modèle de domaine | Modèle de persistance |
|---|---|---|
| Objectif | Représenter les concepts métier | Stocker et retrouver efficacement |
| Structure | Objets riches avec comportement | Tables normalisees ou denormalisees |
| Héritage | Hiérarchies de domaine | Stratégies TPH, TPT ou TPC |
| Value objects | Objets immuables (Adresse, Montant) | Colonnes ou tables séparées |
| Agregats | Frontieres de cohérence transactionnelle | Non representes |
| Optimisation | Clarte et expressivite | Performance de lecture/écriture |
Quand fusionner les deux
Pour un CRUD simple sans logique métier complexe, la séparation domaine/persistance est du sur-engineering. On l'introduit quand le domaine a des règles de validation non triviales, des agregats avec des invariants, ou quand les patterns de lecture et d'écriture différent fortement (CQRS).
Normalisation¶
La normalisation organisé les données pour éliminer la redondance et les anomalies de mise à jour. Chaque forme normale ajoute une contrainte supplémentaire.
Les formes normales¶
| Forme | Règle | Violation typique |
|---|---|---|
| 1NF | Chaque attribut est atomique (pas de liste, pas de groupe) | Colonne "téléphones" avec valeurs multiples |
| 2NF | 1NF + chaque attribut non-clé dépend de toute la clé | Attribut qui dépend d'une partie de clé composee |
| 3NF | 2NF + pas de dépendance transitive entre attributs non-clé | VilleClient dépend de CodePostalClient |
| BCNF | Chaque determinant est une clé candidate | Rares cas où 3NF ne suffit pas |
Quand denormaliser¶
La normalisation optimise l'écriture (pas de redondance, pas d'anomalies). Mais la lecture peut en souffrir : des jointures multiples sur des tables bien normalisees sont coûteuses en performance.
On denormalise quand :
- Les patterns de lecture dominent largement les écritures
- Les jointures deviennent un goulot d'étranglement mesure (pas suppose)
- Les données sont stables (les anomalies de mise à jour sont rares)
- On utilise des vues materialisees ou du CQRS pour séparer lecture et écriture
La denormalisation n'est pas un abandon de la rigueur — c'est une décision d'optimisation documentee, avec conscience des compromis. Un ADR est le bon endroit pour la justifier.
Patterns de domaine¶
Au-delà du diagramme de classes, quelques patterns structurent un modèle de domaine robuste.
Entité vs value object¶
Une entité a une identité qui persiste dans le temps (un Client avec un ID). Un value object est défini par ses attributs (une Adresse est identique à une autre Adresse avec les mêmes champs). Cette distinction guide les décisions d'egalite, d'immutabilite et de stockage.
Agregat¶
Un agregat est un groupe d'entités et de value objects avec une racine qui garantit les invariants. On ne modifie jamais un élément interne directement — on passe par la racine. La Commande est la racine de l'agregat qui contient les LignesCommande. On ne crée pas une LigneCommande sans Commande.
Repository¶
Le repository encapsule l'accès aux agregats. Le domaine ne sait pas comment les données sont stockees — il demande au repository de sauvegarder ou retrouver un agregat. C'est la couture entre modèle de domaine et modèle de persistance.
graph TD
DOM[Modele de domaine] --"utilise"--> REPO[Repository interface]
REPO --"implemente par"--> IMPL[Repository SQL]
IMPL --"lit/ecrit"--> DB[(Base de donnees)]
REPO --"implemente par"--> IMPL2[Repository In-Memory]
IMPL2 --"lit/ecrit"--> MEM[Memoire - tests] Erreurs classiques¶
| Erreur | Conséquence | Correction |
|---|---|---|
| Modéliser les tables, pas le domaine | Logique métier dispersee dans les services | Commencer par le domaine, traduire ensuite |
| Héritage profond (> 3 niveaux) | Rigidite, fragilite, couplage fort | Préférer la composition |
| Multiplicites non validees | Données incoherentes en production | Contraintes en base ET dans le domaine |
| Attributs fourre-tout | "metadata : JSON" cache la complexité | Expliciter les champs et leurs types |
| Ignorer les value objects | Duplication de logique (validation d'adresse) | Extraire en objets immuables |
Chapitre suivant : Comportements et états — diagrammes de sequence, machines a états et automates pour modéliser la dynamique du système.