Exemple de diagramme d'états : le cycle de vie d'un abonnement en machine à états

Un diagramme d'états-transitions UML commenté pour un abonnement SaaS : états, événements, gardes, choix, état composite et les bugs qu'il révèle tôt.

· Fondateur et développeur

Publié le

Exemple de diagramme d'états : le cycle de vie d'un abonnement en machine à états

Les bugs d'abonnement sont des bugs d'état. Un client garde son accès après avoir résilié. Un compte en retard de paiement règle sa facture et reste bloqué. Un essai se termine pour quelqu'un sans carte, et aucune règle ne dit ce qui se passe ensuite. Chacun de ces cas est une transition que personne n'a dessinée.

Un diagramme de machine à états, souvent appelé diagramme d'états, liste chaque état dans lequel un objet peut se trouver et chaque événement qui le fait passer à un autre. Les transitions manquantes apparaissent en revue plutôt que dans les tickets du support. Cet article en construit un pour un abonnement SaaS, avec les statuts documentés par Stripe Billing pour que vous puissiez comparer le modèle à un vrai système, et explique la notation au fil de l'eau.

Quand dessiner une machine à états

Dessinez-en une quand un objet unique a un cycle de vie avec des règles : une commande, un abonnement, un paiement, un ticket de support, un déploiement. Les signaux d'alerte sont connus :

  • Une colonne status avec plus de trois valeurs, et du code qui la teste à de nombreux endroits.
  • Des indicateurs booléens comme is_canceled et is_paused qui peuvent se contredire.
  • Des débats pour savoir si quelque chose « peut arriver » depuis un statut donné.

Un diagramme d'états n'est pas la bonne image pour une conversation entre plusieurs participants, où un diagramme de séquence montre l'ordre des messages, ni pour un processus métier avec de nombreuses personnes et passations, où un diagramme BPMN convient mieux.

La notation

Élément Notation Signification
État Rectangle aux coins arrondis Une condition dans laquelle l'objet reste jusqu'à l'arrivée d'un événement
Pseudo-état initial Petit disque plein Là où la machine démarre
État final Disque plein dans un anneau La machine a terminé
Transition Flèche étiquetée event [guard] / action Passage quand l'événement survient et que la garde est vraie
Choix Losange Les gardes des flèches sortantes déterminent la cible
État composite Un état qui contient des états Des règles partagées par tout ce qu'il contient
Comportement interne entry /, exit /, do / S'exécute à l'entrée, à la sortie ou pendant l'état

Chaque partie d'une étiquette de transition est facultative. invoice paid est un événement à lui seul, [setting: unpaid] une garde à elle seule, et cancel / revoke access un événement avec une action. La syntaxe et les symboles viennent de la spécification UML 2.5.1.

Les statuts d'abonnement

L'exemple reprend les statuts de la présentation des abonnements de Stripe, avec des paiements par carte automatiques :

Statut Ce qu'il signifie Accès
incomplete Le premier paiement n'est pas encore fait ; le client a 23 heures Non
incomplete_expired Le premier paiement n'a jamais abouti ; rien n'est facturé Non
trialing En période d'essai Oui
active En règle Oui
past_due Le paiement de la dernière facture a échoué ou n'a pas été tenté À vous de choisir
unpaid Les relances sont épuisées et vos paramètres ont choisi cette issue Non
paused L'essai s'est terminé sans moyen de paiement, et la pause a été choisie Non
canceled Terminé ; Stripe le décrit comme un état terminal Non

Beaucoup de produits maintiennent l'accès pendant past_due tant que les relances de paiement sont en cours, et cet exemple fait de même. Écrivez cette décision sur le diagramme ; c'est celle qu'on oublie.

Construire les transitions

Prenez chaque état et demandez-vous quels événements peuvent s'y produire.

  1. Départ. subscribe [trial] mène à trialing ; subscribe [no trial] mène à incomplete.
  2. Premier paiement. Depuis incomplete, invoice paid mène à active. Si 23 heures passent sans paiement, l'abonnement passe à incomplete_expired et Stripe annule la facture. Cet état est final : pour réessayer, le client démarre un nouvel abonnement.
  3. Fin de l'essai. trial ends mène à un choix. [payment succeeds] mène à active ; [no payment method] mène à paused dans cet exemple. Stripe vous laisse choisir ce qui se passe dans ce cas : créer une facture (par défaut), résilier ou mettre en pause. Dessinez le réglage que vous utilisez vraiment, pas les trois.
  4. Renouvellements. Depuis active, renewal payment fails mène à past_due, et invoice paid le ramène.
  5. Relances épuisées. Un autre choix : selon vos paramètres, l'abonnement passe à unpaid ou canceled, ou reste en past_due. Depuis unpaid, payer la facture le ramène à active.
  6. Sortie. cancel fait passer active à canceled. La résiliation en fin de période n'est pas un état distinct : l'abonnement reste active avec un indicateur jusqu'à la fin de la période, puis passe à canceled.

Un état composite pour l'accès

Ici, trois états donnent accès : trialing, active et past_due. Les dessiner dans un seul état composite, Access granted, vous permet d'écrire la règle une seule fois : entry / grant access et exit / revoke access. Toute transition qui quitte le composite retire l'accès, quel que soit l'état de départ ; aucun chemin ne peut donc l'oublier. Passer d'un état à un autre à l'intérieur du composite, par exemple de trialing à active, ne le quitte pas et n'exécute pas ces actions.

Un point de départ mène à trialing ou à incomplete. Trialing, active et past due sont dans un cadre Access granted. La fin d'essai passe par un choix vers active ou paused. Un renouvellement échoué mène à past due ; les relances épuisées mènent à unpaid ou canceled, puis à l'état final.

15 composants · 17 connexions

Ouvrir dans Archboard
Contenu du diagramme
  • Access granted
  • trialing
  • active
  • past_due
  • paused
  • incomplete
  • incomplete_expired
  • unpaid
  • canceled
  • subscribe [trial]
  • subscribe [no trial]
  • invoice paid
  • after 23 h / void invoice
  • trial ends
  • [payment succeeds]
  • [no payment method]
  • resume [payment succeeds]
  • renewal payment fails
  • retries exhausted
  • [setting: unpaid]
  • [setting: cancel]
  • cancel
Une machine à états pour un abonnement SaaS reprenant les statuts documentés par Stripe Billing : l'essai et le premier paiement, un état composite pour tous les statuts qui donnent accès, les renouvellements échoués et les deux façons dont un abonnement se termine.

Comment le lire :

  • En haut. Le point de départ se sépare selon l'essai : trialing dans le composite, ou incomplete en dehors. Depuis incomplete, le paiement mène à active, et 23 heures sans paiement mènent à incomplete_expired puis à l'état final à droite.
  • Au milieu, Access granted. trial ends atteint un choix : active si le paiement aboutit, paused à gauche s'il n'y a pas de moyen de paiement. Un renouvellement échoué fait passer active à past_due, et le paiement le ramène.
  • En bas. Quand les relances sont épuisées, un second choix envoie l'abonnement vers unpaid ou canceled. unpaid peut encore revenir à active en payant ; canceled se termine dans l'état final.
  • Les liens qui entrent dans le cadre donnent l'accès, ceux qui en sortent le retirent.

Du diagramme au code

Le diagramme se traduit en table de transitions. Tout ce qui n'est pas dans la table est refusé :

type Status = 'incomplete' | 'incomplete_expired' | 'trialing' | 'active'
  | 'past_due' | 'unpaid' | 'paused' | 'canceled';

const transitions: Record<Status, Partial<Record<string, Status>>> = {
  incomplete: { invoice_paid: 'active', expired: 'incomplete_expired' },
  trialing: { trial_ended_paid: 'active', trial_ended_no_method: 'paused' },
  active: { payment_failed: 'past_due', cancel: 'canceled' },
  past_due: { invoice_paid: 'active', retries_exhausted: 'unpaid' },
  unpaid: { invoice_paid: 'active' },
  paused: { resumed: 'active' },
  incomplete_expired: {},
  canceled: {},
};

function next(status: Status, event: string): Status {
  const target = transitions[status][event];
  if (!target) throw new Error(`${event} is not allowed in ${status}`);
  return target;
}

Si les événements viennent des webhooks d'un prestataire de paiement, ne comptez pas sur leur ordre. La documentation des webhooks de Stripe indique qu'il ne garantit pas que les événements arrivent dans l'ordre où ils ont été générés. Traitez chaque événement comme une raison de récupérer le statut actuel de l'abonnement, puis appliquez la transition à celui-ci.

Erreurs courantes

  • Des événements manquants. Pour chaque état, vérifiez chaque événement. « Que se passe-t-il si le client résilie pendant past_due ? » est une question à laquelle le diagramme doit répondre.
  • Des indicateurs dessinés comme des états. La résiliation en fin de période, une remise ou un nombre de sièges sont des propriétés d'un état, pas des états.
  • Des actions dessinées comme des états. « Envoyer l'e-mail de relance » est une action sur une transition ou un comportement entry, pas un endroit où l'abonnement attend.
  • Pas d'état final. Montrez où le cycle de vie se termine, pour que personne n'écrive du code qui ressuscite un abonnement résilié.
  • Des gardes qui se chevauchent ou laissent des trous. Les gardes qui sortent d'un choix doivent couvrir chaque cas exactement une fois ; ajoutez [else] si besoin.
  • Deux cycles de vie dans un diagramme. L'abonnement et ses factures ont des machines à états distinctes. Reliez-les par des événements au lieu de les fusionner.

Gardez le diagramme à côté du code qui l'implémente et relisez les deux dans la même pull request, comme l'explique l'article des diagrammes d'architecture qui ne se périment pas.

Le construire dans Archboard

  1. Connectez-vous avec Google ou GitHub et créez un diagramme.
  2. Depuis le pack UML, ajoutez un État pour chaque statut.
  3. Ajoutez un Cadre du pack Base nommé Access granted autour de trialing, active et past_due.
  4. Ajoutez une petite Ellipse pleine du pack Base pour l'état initial, un Losange pour chaque choix et une Icône de cercle pointé pour chaque état final.
  5. Reliez les états par des liens étiquetés event [guard] / action.
  6. Ajoutez une Note UML avec les actions d'entrée et de sortie de l'état composite.

L'exemple utilise 14 nœuds de diagramme et reste dans les limites de l'offre Free. Avec un agent IA connecté via MCP, vous pouvez coller votre enum de statuts et le laisser esquisser les états, puis vérifier vous-même chaque transition.

FAQ

Quelle est la différence entre un diagramme d'états et un logigramme ? Un logigramme montre les étapes d'un processus dans l'ordre. Un diagramme d'états montre les conditions dans lesquelles un objet peut se trouver et les événements qui le font passer de l'une à l'autre ; il répond donc à ce qui est permis plutôt qu'à ce qui vient ensuite.

Chaque valeur de ma colonne de statut doit-elle être un état ? Chaque valeur qui change le comportement, oui. Si deux valeurs se comportent de la même façon dans tous les cas, fusionnez-les ; si une valeur cache deux comportements, scindez-la.

Où se placent les webhooks dans une machine à états ? Ce sont des événements. Vérifiez-les, récupérez le statut actuel auprès du prestataire et appliquez la transition, car les événements peuvent arriver dans le désordre ou plusieurs fois.

Ai-je besoin d'un état composite ? Utilisez-en un quand plusieurs états partagent une règle, comme l'accès ou la facturation. Il transforme une règle que vous répéteriez sur de nombreuses flèches en une action d'entrée et une action de sortie.

Quel niveau de détail pour les étiquettes de transition ? Nommez l'événement en termes métier et n'ajoutez une garde que lorsque deux flèches partent sur le même événement. Mettez sur le diagramme les actions qui comptent pour la justesse, comme l'octroi de l'accès.

Commencez gratuitement — un siège, sans carte bancaire.

Articles associés

← Retour au blog

Ouvrez un canevas en une minute

Connectez-vous avec Google ou GitHub. Gratuit pour un siège. Passez à un forfait supérieur quand l’équipe vous rejoint.