L’architecture devient concrète quand on veut enlever quelque chose
Quand je dessine deux boîtes séparées dans un diagramme, j’aimerais qu’elles soient réellement séparées. Une simple requête directe suffit pourtant pour que l’application B connaisse les tables privées de l’application A. La flèche paraît propre; le schéma SQL devient un contrat non déclaré.
// B dépend du rangement interne de A.
var cmd = new SqlCommand(
"SELECT statut FROM moduleA_commandes WHERE id = @id", conn);
// Un contrat limite ce que B doit connaître.
public interface ICommandeService
{
StatutCommande ObtenirStatut(int commandeId);
}
Le raccourci fonctionne jusqu’au moment où A renomme la colonne, sépare les commandes actives des archives ou change la signification de statut. B peut alors casser sans avoir été modifiée. Pire, si B écrit directement dans la table, elle peut contourner les validations et laisser A dans un état impossible.
Un service place une frontière. La requête arrive à une façade, qui valide l’identifiant, applique les règles et lit son propre stockage. La réponse expose un statut métier plutôt qu’une ligne de table.
B -> ICommandeService -> règles de A -> tables de A
<- réponse versionnée <-
Un contrat doit aussi survivre aux pannes
L’interface n’est pas gratuite. Un appel local devient peut-être un appel réseau avec délai, timeout et indisponibilité. B doit décider si le statut peut être mis en cache, combien de temps et quoi faire si A ne répond pas. Réessayer aveuglément une commande d’écriture peut la créer deux fois; une opération importante a besoin d’un identifiant de requête ou d’un contrôle d’idempotence.
Pour une lecture, je pourrais accepter le dernier statut connu pendant cinq minutes et l’afficher comme tel. Pour une décision de crédit, une valeur périmée est peut-être inacceptable. La politique vient du sens métier, pas du protocole.
Le contrat a aussi une version. Ajouter un champ facultatif est généralement plus facile que renommer un champ existant. Lors d’un changement incompatible, A peut servir les versions 1 et 2 pendant une transition, mesurer les appels restants, puis retirer la vieille version à une date annoncée.
La cohérence entre deux systèmes demande une autre décision. Une transaction SQL ne peut plus facilement englober A et B. Pour transmettre un changement, A peut enregistrer la commande et un message à publier dans la même transaction locale. Un processus lit ensuite cette table de sortie et envoie le message. Si le réseau tombe, le message reste à reprendre sans perdre la commande.
BEGIN TRANSACTION;
UPDATE commandes SET statut = @statut WHERE id = @id;
INSERT INTO messages_sortants(type, reference_id, statut)
VALUES ('CommandeModifiee', @id, 'AEnvoyer');
COMMIT;
Je ne proposerais pas de placer un service devant chaque table pour embellir Visio. Je chercherais les liens qui empêchent une décision réelle : un serveur impossible à fermer, plusieurs applications qui écrivent la même donnée ou trois déploiements toujours couplés. On peut alors défaire une dépendance pendant un changement déjà prévu et ajouter des tests de contrat entre producteur et consommateurs.
Les journaux de connexion SQL et l’inventaire des comptes de service peuvent confirmer les liens que les diagrammes ont oubliés. Avant de fermer l’accès direct, je mesurerais les requêtes restantes et contacterais leurs propriétaires; le trafic réel complète la mémoire de l’équipe.
Le résultat est volontairement peu spectaculaire : A change son stockage, B continue de recevoir un statut compris, et une panne réseau produit une reprise connue plutôt qu’une donnée silencieusement perdue. C’est à ce moment que l’architecture cesse d’être un dessin et devient une option opérationnelle.