UML

UML — Unified Modeling Language — a été normalisé en 1997 pour unifier les notations objet concurrentes de l’époque. Quatorze types de diagrammes, une spécification de plus de huit cents pages, et une décennie pendant laquelle il fut imposé comme livrable de projet.

Cette imposition l’a tué. Produire des diagrammes complets et à jour coûtait plus que ce qu’ils rapportaient, les outils de génération produisaient des schémas illisibles, et le mot lui-même est devenu, dans beaucoup d’équipes, synonyme de paperasse.

Ce serait dommage de s’arrêter là, parce qu’il en reste quelque chose d’utile :

UML n’est pas un livrable, c’est un vocabulaire de notation. Trois de ses quatorze diagrammes valent la peine d’être connus, pour expliquer quelque chose à quelqu’un — sur un tableau blanc, dans une description de correctif, dans un ADR.

Ce qui survit, et ce qu’on ignore

DiagrammeVerdictPour quoi
SéquenceÀ connaîtreUne interaction, un protocole, un enchaînement dans le temps
ÉtatsÀ connaîtreUn cycle de vie, une machine à états
ClassesÀ connaître, sans le maintenirExpliquer ponctuellement une structure
ActivitéMarginalUn processus métier — un event storming fait mieux
Composants, déploiementRemplacésLe modèle C4 fait la même chose en plus clair
Cas d’utilisationÀ ignorerLe concept est utile, le diagramme n’apporte rien
Objets, paquets, temps, communication, vue d’ensemble des interactions, profils, structure compositeÀ ignorerBeaucoup de notation, très peu d’usage

Les trois premiers se justifient par une propriété commune : ils montrent quelque chose qu’un texte exprime mal. Une séquence d’appels, un ensemble de transitions autorisées, un réseau de relations entre types — pour ces trois là, le dessin est plus dense que la phrase. Pour tout le reste, la phrase gagne.

Tip

Le complément naturel est C4, et les deux sont orthogonaux : C4 répond à « à quel niveau d’abstraction je me place » — personne, système, conteneur, composant — et UML à « avec quelle notation je le dessine ». C4 autorise explicitement la notation UML, et son niveau 4 est un diagramme de classes — que C4 recommande d’ailleurs de ne pas maintenir.

Le diagramme de séquence

Le plus utile des trois, et de loin. Il montre qui appelle qui, dans quel ordre, ce que ni un diagramme de classes ni une vue statique ne peuvent exprimer.

sequenceDiagram
    autonumber
    actor Client
    participant Web as Application web
    participant API
    participant Paiement as Prestataire de paiement
    participant Bus as Bus d'événements

    Client->>Web: Choisit des places
    Web->>API: POST /reservations
    API->>API: Vérifie la capacité de la séance
    alt Places disponibles
        API->>Paiement: Demande d'autorisation
        Paiement-->>API: Autorisé
        API-->>Web: 201 Created
        API--)Bus: ReservationConfirmee
    else Séance complète
        API-->>Web: 409 Conflict
    end
    Web-->>Client: Confirmation ou refus

La notation qui compte réellement :

ÉlémentNotationSens
Message synchroneFlèche pleine ->>L’appelant attend la réponse
RéponseTrait pointillé -->>Le retour de l’appel
Message asynchroneFlèche ouverte --)L’appelant n’attend pas
Auto-appelFlèche vers soi-mêmeUn traitement interne qu’on veut montrer
alt / elseCadreDes chemins exclusifs
optCadreUn chemin conditionnel unique
loopCadreUne répétition
parCadreDes appels parallèles

La distinction synchrone / asynchrone est celle qui apporte le plus d’information sur un diagramme d’architecture : elle rend visible, d’un coup d’œil, où la disponibilité est couplée — sujet traité dans Monolithe, modulaire, microservices. Sur le diagramme ci-dessus, la flèche ouverte vers le bus dit que la réservation n’attend pas la mise à jour des points de fidélité.

Le diagramme d’états

Pour un cycle de vie. C’est le diagramme qui documente le mieux un agrégat dont l’état évolue, et il rend un service que le code rend mal : rendre visibles les transitions qui n’existent pas.

stateDiagram-v2
    [*] --> Provisoire : ouvrir()
    Provisoire --> Confirmee : confirmer()
    Provisoire --> Expiree : après 20 minutes
    Confirmee --> Annulee : annuler(motif)
    Confirmee --> Honoree : contrôle à l'entrée
    Annulee --> [*]
    Expiree --> [*]
    Honoree --> [*]

    Provisoire : Provisoire<br/>places bloquées
    Confirmee : Confirmée<br/>paiement encaissé

Ce diagramme dit quelque chose qu’aucune liste de méthodes ne dit : il n’y a pas de transition de Expiree vers Confirmee, ni de Honoree vers Annulee. C’est exactement ce que le patron State encode dans le typage, et le diagramme est le moyen le plus rapide de le faire valider par un expert du domaine.

Il a aussi un usage détourné très utile : le dessiner à partir du code existant fait apparaître les états intermédiaires involontaires — ceux qu’un booléen de plus a créés sans que personne l’ait décidé.

Le diagramme de classes

Utile ponctuellement, à ne pas maintenir. La notation des relations est la seule partie qu’il faut vraiment connaître, parce que c’est celle que tout le monde utilise de travers.

classDiagram
    class Reservation {
        -ReservationId id
        -EtatReservation etat
        +ouvrir(SeanceId, ClientId)
        +ajouterPlaces(Categorie, Quantite, Montant)
        +confirmer()
        +total() Montant
    }
    class LigneReservation {
        -Quantite quantite
        -Montant prixUnitaire
        +total() Montant
    }
    class Montant {
        <<value object>>
        -int centimes
        +plus(Montant) Montant
    }
    class CommandeRepository {
        <<interface>>
        +parId(ReservationId) Reservation
        +enregistrer(Reservation)
    }
    class PostgresRepository

    Reservation "1" *-- "1..*" LigneReservation : composition
    LigneReservation --> Montant : utilise
    PostgresRepository ..|> CommandeRepository : implémente
RelationNotation UMLSens
HéritageTrait plein, triangle creuxB est un sous-type de A
ImplémentationTrait pointillé, triangle creuxB implémente l’interface A
CompositionTrait plein, losange pleinA possède B : détruire A détruit B
AgrégationTrait plein, losange creuxA contient B, qui peut exister sans lui
AssociationTrait plein, flèche ouverteA connaît B
DépendanceTrait pointillé, flèche ouverteA utilise B sans le détenir

Les multiplicités — 1, 0..1, *, 1..* — se placent aux extrémités et portent souvent plus d’information que la relation elle-même : 1..* sur les lignes d’une réservation dit qu’une réservation vide n’existe pas, ce qui est un invariant métier.

Warning

La distinction agrégation / composition est une source inépuisable de débats stériles, et la spécification elle-même est ambiguë sur l’agrégation. Le critère utilisable est celui du cycle de vie : composition si détruire le tout détruit les parties, association sinon. Et en pratique, l’agrégation à losange creux n’apporte presque rien — s’en passer ne fait perdre aucune information utile.

À l’inverse, la composition est significative en conception : c’est exactement la frontière d’un agrégat au sens DDD.

Comment les écrire aujourd’hui

Pas dans un outil graphique. Le diagramme vit dans le dépôt, en texte, à côté de ce qu’il décrit — pour les mêmes raisons que les ADR : il passe en revue, il se compare d’une version à l’autre, il survit aux migrations d’outils.

OutilNote
MermaidRendu nativement par GitHub, GitLab et la plupart des générateurs de sites — dont celui-ci. Le plus simple, et suffisant pour les trois diagrammes de cette page
PlantUMLCouverture UML bien plus complète, rendu plus fidèle à la norme, mais demande un serveur ou un binaire
StructurizrPour du C4, et exporte vers les deux précédents — voir Mise en œuvre
Outils graphiquesExcalidraw, draw.io : parfaits pour un schéma jetable en réunion. À ne pas confondre avec de la documentation

Tous les diagrammes de cette page sont écrits en Mermaid, dans le fichier source de la page — vous pouvez en juger le coût d’écriture : entre cinq et quinze lignes chacun.

Quand s’en servir

Oui : sur un tableau blanc pour se mettre d’accord à trois ; dans la description d’une demande de fusion, pour expliquer un enchaînement qu’on modifie ; dans un ADR, pour montrer l’option retenue ; dans le fichier README d’un module, pour son cycle de vie ; pour faire valider une machine à états par un expert du domaine.

Non : comme livrable de projet ; généré automatiquement depuis le code — le résultat est illisible et n’ajoute rien à ce que le code dit déjà ; pour documenter exhaustivement un système ; pour les onze autres types de diagrammes.

La règle qui résume la page : un diagramme UML doit répondre à une question précise. S’il n’y a pas de question, il n’y a pas besoin de diagramme.

Références