# Architecture hexagonale pour une extension WordPress : quand ça vaut le coup

> Isoler le domaine métier d'une extension WordPress derrière des ports et des adaptateurs améliore la testabilité, mais a un coût réel qu'il faut savoir peser.

- Auteur : Clément Hadrot
- Publié le : 2025-03-21
- Mis à jour le : 2025-03-21
- Catégorie : Extensions
- URL : https://wpmoderne.dev.wordpress-developpement.fr/extensions/architecture-hexagonale-extension-wordpress/

## L’essentiel

- Le domaine métier ne connaît aucune fonction WordPress
- Les adaptateurs traduisent entre WordPress et le domaine, dans les deux sens
- Le surcoût se justifie surtout sur une logique métier riche et testée

Une extension de calcul de commissions pour un réseau de mandataires immobiliers, développée pour un client il y a deux ans, contenait une règle métier non triviale : le taux de commission dépend de l'ancienneté du mandataire, du montant de la transaction, d'un plafond annuel, et d'exceptions négociées au cas par cas. Cette logique était éparpillée dans des fonctions accrochées à des hooks WooCommerce, mélangée à des appels à `get_post_meta()` et `wc_get_order()`. Tester une règle de calcul isolément supposait de charger tout WordPress, WooCommerce, et une base de données de test — pour vérifier une simple formule arithmétique.

C'est exactement le problème que l'architecture hexagonale, aussi appelée « ports et adaptateurs », cherche à résoudre : isoler la logique métier de tout ce qui l'entoure, pour pouvoir la tester, la faire évoluer et la comprendre indépendamment du framework qui l'héberge.

## Le principe en une phrase

Le domaine métier — les règles, les calculs, les décisions propres au métier de l'extension — ne doit jamais appeler directement une fonction WordPress. Il communique avec l'extérieur uniquement via des interfaces qu'il définit lui-même, appelées « ports ». Le code qui sait parler à WordPress — hooks, `get_post_meta()`, requêtes SQL — vit dans des « adaptateurs » qui implémentent ces interfaces, mais restent en dehors du domaine.

## Arborescence type

> L'essentiel à retenir : Le domaine métier ne connaît aucune fonction WordPress ; Les adaptateurs traduisent entre WordPress et le domaine, dans les deux sens ; Le surcoût se justifie surtout sur une logique métier riche et testée

```
mon-extension-commissions/
├── src/
│   ├── Domaine/
│   │   ├── Commission.php              # objet métier, aucune dépendance WordPress
│   │   ├── CalculateurCommission.php    # la règle de calcul pure
│   │   └── Port/
│   │       ├── DepotMandataire.php      # interface (port)
│   │       └── DepotTransaction.php     # interface (port)
│   ├── Adaptateur/
│   │   ├── DepotMandataireWordPress.php # implémente DepotMandataire via WP_User
│   │   └── DepotTransactionWooCommerce.php
│   └── Bootstrap.php                    # relie les hooks WordPress aux ports
└── mon-extension-commissions.php
```

## Le domaine : du PHP pur, sans hook ni fonction WordPress

```
namespace MonExtension\Domaine;

final class CalculateurCommission {
    public function __construct( private DepotMandataire $mandataires ) {}

    public function calculer( string $mandataireId, float $montantTransaction ): Commission {
        $mandataire = $this->mandataires->trouver( $mandataireId );
        $taux       = $this->determinerTaux( $mandataire->anciennete() );
        $montant    = min( $montantTransaction * $taux, $mandataire->plafondAnnuelRestant() );

        return new Commission( $mandataireId, $montant, $taux );
    }

    private function determinerTaux( int $anneesAnciennete ): float {
        return match ( true ) {
            $anneesAnciennete >= 5 => 0.08,
            $anneesAnciennete >= 2 => 0.06,
            default                => 0.04,
        };
    }
}
```

Cette classe ne connaît ni `WP_Post`, ni `get_post_meta`, ni WooCommerce. Elle dépend uniquement de l'interface `DepotMandataire`, un port qu'elle définit elle-même : « je sais calculer une commission si on me fournit un moyen de trouver un mandataire ».

## Les adaptateurs : la traduction vers WordPress

```
namespace MonExtension\Adaptateur;

use MonExtension\Domaine\Port\DepotMandataire;
use MonExtension\Domaine\Mandataire;

final class DepotMandataireWordPress implements DepotMandataire {
    public function trouver( string $mandataireId ): Mandataire {
        $utilisateur = get_userdata( (int) $mandataireId );
        $anciennete  = (int) get_user_meta( $utilisateur->ID, 'anciennete_annees', true );
        $plafond     = (float) get_user_meta( $utilisateur->ID, 'plafond_annuel_restant', true );

        return new Mandataire( $mandataireId, $anciennete, $plafond );
    }
}
```

C'est uniquement ici, dans l'adaptateur, que vivent les appels à l'API WordPress. Si demain l'extension doit fonctionner sur un site qui stocke les mandataires dans une table personnalisée plutôt que via `WP_User`, seul cet adaptateur change — le domaine et sa logique de calcul restent intouchés.

## Ce que ça apporte concrètement

Le bénéfice le plus immédiat : la classe `CalculateurCommission` se teste avec de simples doubles de test (des implémentations en mémoire de `DepotMandataire`), sans base de données ni WordPress chargé du tout. Un test unitaire s'exécute en quelques millisecondes plutôt qu'en secondes, et surtout, il teste une règle métier isolément, sans bruit d'infrastructure autour.

```
$mandataires = new DepotMandataireEnMemoire( array(
    '42' => new Mandataire( '42', anciennete: 6, plafondAnnuelRestant: 5000.0 ),
) );

$calculateur = new CalculateurCommission( $mandataires );
$commission  = $calculateur->calculer( '42', 10000.0 );

assert( $commission->montant() === 800.0 ); // 8 % de 10 000
```

## Le coût réel, sans le minimiser

Cette architecture n'est pas gratuite. Elle demande d'écrire des interfaces, des implémentations, et une couche de câblage (le `Bootstrap` qui relie les hooks WordPress aux ports) là où un code procédural classique aurait suffi. Pour une extension dont la logique métier tient en dix lignes — afficher un shortcode, ajouter un champ à un formulaire — cette structure est une complexité inutile qui ralentit le développement sans bénéfice mesurable.

> La question qu'on se pose avant de choisir cette architecture : si la règle métier changeait souvent, ou si elle méritait vraiment d'être testée finement, est-ce que ça vaudrait le coût de la séparation ? Sur l'extension de commissions, la réponse était clairement oui : la formule a évolué quatre fois en deux ans, sans jamais casser un test existant.

## Quand ça vaut le coup, et quand ça ne le vaut pas

- **Ça vaut le coup** : logique métier riche et amenée à évoluer (calculs financiers, règles de tarification, moteurs de règles), besoin réel de tests unitaires rapides, ou perspective de faire tourner la même logique sur plusieurs plateformes (WordPress aujourd'hui, une API headless demain).
- **Ça ne vaut pas le coup** : extension à fonctionnalité unique et stable, prototype, ou logique qui se limite à afficher et transformer des données sans règle de décision complexe.

## Notre verdict

L'architecture hexagonale n'est pas une norme à appliquer par principe à toute extension WordPress : c'est un outil pour un problème précis, celui d'une logique métier suffisamment riche pour mériter d'être isolée et testée indépendamment du framework. Sur l'extension de commissions, elle a transformé une logique fragile et difficile à faire évoluer en un cœur métier stable, avec une suite de tests qui tourne en quelques secondes. Sur une extension plus simple, elle aurait été un fardeau architectural sans justification.
