Aller au contenu

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 :

  1. Nom : le concept métier (PascalCase par convention)
  2. Attributs : les données que l'entité porte, avec type et visibilité
  3. 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.