vendredi 25 septembre 2026

À propos

Contact

Extensions

Extensions et event sourcing léger : journaliser plutôt qu’écraser

Certaines données métier valent surtout par leur historique. Voici comment conserver chaque changement plutôt que d'écraser silencieusement l'état précédent.

Par Clément Hadrot • 6 mai 2026 • 5 min de lecture • Aucun commentaire
Extensions et event sourcing léger : journaliser plutôt qu'écraser

Une extension de gestion de contrats pour une coopérative agricole stockait le statut de chaque contrat dans une simple colonne, mise à jour à chaque changement : brouillon, signé, résilié. Un litige avec un adhérent a nécessité de prouver la date exacte à laquelle un contrat était passé de « signé » à « résilié », et par quel utilisateur cette modification avait été effectuée. Cette information n’existait plus nulle part : la colonne ne conservait que la dernière valeur, sans aucune trace des changements précédents.

Ce cas illustre une limite fréquente des architectures qui se contentent de stocker un état courant : dès qu’une question porte sur le passé plutôt que sur le présent, la donnée manque, alors qu’elle aurait pu être conservée sans effort disproportionné. L’event sourcing léger répond précisément à ce besoin, sans nécessiter d’adopter un framework complet dédié à ce patron d’architecture.

Le principe, sans le framework

L’event sourcing complet, tel qu’on le rencontre dans certaines architectures logicielles ambitieuses, repose sur l’idée que l’état d’un système ne devrait jamais être stocké directement, mais reconstruit à la demande à partir de la séquence complète des événements qui l’ont produit. Appliqué intégralement, ce principe demande une infrastructure conséquente, des snapshots périodiques, une gestion fine du rejeu d’événements.

Pour une extension WordPress classique, une version bien plus légère suffit largement : conserver un journal d’événements append-only à côté de l’état courant, plutôt qu’à la place de celui-ci. L’état courant continue d’exister, mis à jour normalement pour les besoins d’affichage habituels, mais chaque changement laisse également une trace immuable dans une table dédiée.

La table d’événements

global $wpdb;

$table = $wpdb->prefix . 'contrats_evenements';

$sql = "CREATE TABLE $table (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    contrat_id BIGINT UNSIGNED NOT NULL,
    type_evenement VARCHAR(50) NOT NULL,
    donnees LONGTEXT NOT NULL,
    utilisateur_id BIGINT UNSIGNED NOT NULL,
    survenu_le DATETIME NOT NULL,
    PRIMARY KEY (id),
    KEY contrat_id (contrat_id)
) " . $wpdb->get_charset_collate() . ";";

Aucune ligne de cette table n’est jamais modifiée ni supprimée après son insertion : c’est le sens du terme append-only. Chaque changement de statut, chaque modification de montant, chaque action significative sur un contrat, produit une nouvelle ligne, sans jamais toucher aux précédentes.

Enregistrer un événement à chaque changement

L'essentiel à retenir : Écraser une valeur fait perdre la question de pourquoi elle a changé ; Une table d'événements append-only suffit sans framework dédié ; L'état courant reste une simple projection reconstruite à la demande
function contrats_changer_statut( int $contrat_id, string $nouveau_statut, int $utilisateur_id ) {
    global $wpdb;

    $ancien_statut = get_post_meta( $contrat_id, 'statut', true );

    update_post_meta( $contrat_id, 'statut', $nouveau_statut ); // état courant, comme avant

    $wpdb->insert( $wpdb->prefix . 'contrats_evenements', array(
        'contrat_id'     => $contrat_id,
        'type_evenement' => 'changement_statut',
        'donnees'        => wp_json_encode( array(
            'ancien_statut' => $ancien_statut,
            'nouveau_statut' => $nouveau_statut,
        ) ),
        'utilisateur_id' => $utilisateur_id,
        'survenu_le'     => current_time( 'mysql' ),
    ) );
}

Cette fonction ne remplace rien de l’existant : elle ajoute simplement, à côté de la mise à jour habituelle du statut, un enregistrement immuable de ce changement précis, avec son contexte complet. Pour le litige mentionné en introduction, cette seule table aurait suffi à répondre à la question posée par l’adhérent, avec la date exacte et l’utilisateur responsable du changement.

L’état courant comme simple projection

Une conséquence intéressante de cette approche, une fois adoptée, est que l’état courant peut à tout moment être reconstruit entièrement à partir du seul journal d’événements, ce qui offre un filet de sécurité en cas de corruption de la donnée courante, ou simplement un moyen de vérifier sa cohérence :

function contrats_reconstruire_statut( int $contrat_id ) : string {
    global $wpdb;

    $dernier_evenement = $wpdb->get_row( $wpdb->prepare(
        "SELECT donnees FROM {$wpdb->prefix}contrats_evenements
         WHERE contrat_id = %d AND type_evenement = 'changement_statut'
         ORDER BY id DESC LIMIT 1",
        $contrat_id
    ) );

    if ( ! $dernier_evenement ) {
        return 'brouillon'; // statut initial par défaut
    }

    $donnees = json_decode( $dernier_evenement->donnees, true );
    return $donnees['nouveau_statut'];
}

Où ce patron devient pertinent, où il ne l’est pas

  • Un contrat, une commande, un dossier de subvention, tout ce qui peut faire l’objet d’un litige ou d’un audit ultérieur, gagne à conserver son historique complet plutôt que son seul état final.
  • Un compteur de vues d’article ou un cache de rendu n’a, à l’inverse, aucun besoin de ce traitement : leur valeur courante suffit largement, et journaliser chaque incrément produirait un volume de données disproportionné pour un bénéfice nul.
  • La table d’événements doit rester purement additive dans le code : toute tentation de la modifier après coup, même pour corriger une erreur de saisie, casse la garantie d’immuabilité qui fait tout l’intérêt de l’approche.

Une question qui nous sert désormais de filtre avant d’ajouter ce type de journal : si un client demandait dans six mois « qui a changé cette valeur, et quand », le code actuel pourrait-il répondre ? Si la réponse est non, la donnée mérite probablement son propre journal d’événements.

En résumé

L’event sourcing complet reste disproportionné pour la grande majorité des extensions WordPress, mais sa version allégée, une simple table append-only à côté de l’état courant habituel, coûte peu à mettre en place et répond à un besoin réel dès qu’une donnée métier importante fait un jour l’objet d’une question sur son passé plutôt que sur sa seule valeur actuelle. Le coût de ne pas l’avoir prévu, lui, se révèle toujours au pire moment, quand la réponse ne peut plus être reconstituée.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi