Domaines et cycles de vie - 06 - Single Source of Truth (SSOT)

Pourquoi l'état d'une entité doit vivre a un seul endroit. SSOT, état persiste vs derive, et bonnes pratiques backend pour le cycle de vie.

06 - Single Source of Truth (SSOT)

Ce que tu vas apprendre

  • Ce que signifie "source unique de vérité" et pourquoi c'est non negociable
  • Ce qui arrive quand l'état est disperse
  • La différence entre état persiste et état derive
  • Comment implementer une SSOT en pratique

Prerequisites


Le principe

Single Source of Truth (SSOT), ou "source unique de vérité", signifie que l'état d'une entité doit etre stocke a un seul endroit. Cet endroit est autoritaire : quand il dit que la Place est en ENRICHED, c'est ENRICHED. Point.

Tous les autres systèmes (frontend, cache, moteur de recherche, API) doivent s'aligner sur cette source. S'ils ont une information différente, c'est eux qui ont tort, pas la source de vérité.

La regle est simple : si tu as plus d'une source de vérité, tu n'as pas de source de vérité.

Ce qui arrive quand l'état est disperse

Voici un cas réel. Dans un système sans SSOT, l'état d'une Place pendant un batch est disperse dans quatre endroits :

Système Ce qu'il stocke Problème
React state (RAM) L'action en cours (pending_images, needs_review) Perdu au refresh de la page
IndexedDB (navigateur) Le statut de review Local a ce navigateur, pas partageable
Firestore Les paramètres de configuration Ne connaît pas l'état de la Place
PostgreSQL La Place finale (si publiee) Ne sait pas qu'un batch est en cours

Consequences concrètes :

  • Tu ouvres un autre navigateur : ta session de review a disparu
  • Un collegue veut reprendre ton travail : il ne peut pas, l'état est dans TON navigateur
  • Quelqu'un demande "combien de Places sont en attente de review ?" : personne ne sait répondre
  • Un bug survient : impossible de savoir dans quel état etait la Place au moment du bug

Avec une SSOT

Quand l'état vit a un seul endroit (ici, PostgreSQL), tout devient simple :

sql-- Combien de Places sont dans chaque etat ?
SELECT status, COUNT(*) FROM places GROUP BY status;

-- Ou en est la Place 42 ?
SELECT id, name, status FROM places WHERE id = 'place_42';

-- Quelles Places sont pretes a etre publiees ?
SELECT id, name FROM places WHERE status = 'READY_FOR_PUBLICATION';

Un seul SELECT. Pas besoin de croiser quatre systèmes. Pas besoin de recalculer. La vérité est la.

État persiste vs état derive

C'est une distinction fondamentale.

État persiste (persisted state)

L'état est stocke explicitement dans un champ. C'est un fait : "cette Place est en ENRICHED". Ca ne depend de rien d'autre.

typescript// Etat persiste : on le lit directement
const place = await db.findById("place_42");
console.log(place.status); // "ENRICHED" -- c'est la verite

État derive (derived state)

L'état est recalcule à partir d'autres donnees. Ce n'est pas stocke, c'est deduit.

typescript// Etat derive : on recalcule a chaque fois
function getPlaceStatus(place: RawPlace): string {
  if (place.is_closed) return "DELETED";
  if (place.images.length >= 3 && place.categories.length > 0) return "READY";
  if (place.categories.length > 0) return "ENRICHED";
  return "DRAFT";
}

Le problème de l'état derive : il depend de la logique de calcul. Si la logique change, l'état change. Deux endroits du code peuvent calculer un état différent pour la meme entité. Il n'y a pas de vérité, il y a des opinions.

Quand utiliser l'état derive ?

L'état derive n'est pas toujours mauvais. Il est utile pour des choses non critiques :

  • Un badge "nouveau" sur une Place créée il y a moins de 24h (derive de createdAt)
  • Un pourcentage de completion d'un formulaire (derive des champs remplis)

Mais l'état du cycle de vie doit toujours etre persiste. Toujours.

Anti-pattern : le cache comme source de vérité

Un piège classique : utiliser un cache (Redis, IndexedDB, state React) comme source de vérité. Le cache est une copie pour la performance. Il peut etre perime, vide, ou incorrect. Ce n'est pas une source de vérité.

   Source de verite          Cache (copie)
   +-----------------+         +-------------+
   | PostgreSQL      |-------->| Redis       |
   | status =        |         | status =    |
   | "ENRICHED"      |         | "DRAFT"     |  <-- perime !
   +-----------------+         +-------------+

Si le cache dit DRAFT et la base dit ENRICHED, c'est la base qui a raison. Toujours.

En pratique

Pour implementer une SSOT :

  1. Choisis un système autoritaire (généralement ta base de donnees principale)
  2. Toutes les transitions ecrivent dans ce système
  3. Tous les autres systèmes lisent depuis ce système (ou depuis un cache qu'ils savent etre une copie)
  4. Jamais de mise à jour d'état depuis un système secondaire

C'est un principe qu'on applique sur paltemps.fr : chaque entité a une source de vérité unique, et tous les autres systèmes s'y referent.


Résumé

  • SSOT : l'état d'une entité vit a un seul endroit, autoritaire et consultable par tous
  • L'état disperse (plusieurs systèmes avec chacun "sa" version) rend le debug impossible
  • L'état persiste est un fait stocke. L'état derive est une opinion calculee.
  • Le cycle de vie doit toujours etre en état persiste
  • Un cache est une copie, jamais une source de vérité

Article précédent : 05 - Transitions, guards et side effects Article suivant : 07 - Les invariants : ce qui doit toujours etre vrai

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.