Développement web

Service layer, use cases et orchestrateurs : comment ne pas tout mélanger

Jérémy Chomel Dawap
  • Publié le : 17 avril 2026
  • Mis à jour le : 18 août 2026
  • Temps de lecture : 16 minutes
  1. Distinguer décision métier, cas d’usage et coordination
  2. Repérer une couche de services devenue fourre-tout
  3. Donner une responsabilité nette à la service layer
  4. Écrire un cas d’usage autour d’une intention observable
  5. Réserver l’orchestrateur aux processus distribués
  6. Choisir une frontière transactionnelle explicite
  7. Découper un traitement de commande sans ambiguïté
  8. Tester chaque niveau avec la bonne preuve
  9. Instrumenter la coordination pour le support
  10. Pour qui cette séparation devient prioritaire
  11. Erreurs fréquentes et contre-mesures
  12. Décider sans ajouter une couche artificielle
  13. Plan d’action sur deux itérations
  14. Guides complémentaires pour fiabiliser l’architecture
  15. Conclusion : des frontières lisibles jusque dans le run
Portrait de Jérémy Chomel

Une méthode nommée process() valide une commande, calcule un prix, écrit trois tables, appelle le paiement, envoie un courriel et publie un événement. L’équipe l’appelle « service », mais personne ne sait plus si elle contient une règle métier, un cas d’usage ou la coordination de plusieurs systèmes. Le problème n’est pas le vocabulaire : lors d’un incident, il devient impossible de dire quelle étape peut être rejouée sans débiter deux fois le client.

Le vrai enjeu est simple : une frontière d’architecture n’est utile que si elle rend une décision, une transaction et une reprise plus faciles à expliquer. La service layer expose les capacités de l’application, le cas d’usage porte une intention métier complète, et l’orchestrateur coordonne des participants qui ne partagent pas la même transaction. Ces rôles peuvent tenir dans deux classes pour un produit modeste ; ils ne justifient pas automatiquement trois répertoires et quinze interfaces.

Deux signaux faibles révèlent le mélange avant la panne. Les tests d’un changement banal doivent simuler la moitié du système, puis le support demande régulièrement à un développeur « jusqu’où le traitement est allé ». Le coût caché se lit dans les diagnostics, les reprises manuelles et la peur de modifier une classe centrale, bien davantage que dans son nombre de lignes.

Ce guide propose des critères concrets, un exemple de commande, des seuils locaux et un plan de refactorisation réversible. Il s’inscrit dans une démarche de développement web sur mesure où l’architecture reste au service des opérations, des utilisateurs et du produit.

Distinguer décision métier, cas d’usage et coordination

La décision métier répond à une question indépendante du canal : cette commande peut-elle être confirmée, ce dossier peut-il changer de statut, cette remise est-elle autorisée ? Elle appartient au modèle de domaine, avec les invariants et les données nécessaires pour trancher. Elle ne devrait connaître ni contrôleur HTTP, ni file de messages, ni fournisseur de paiement.

Le cas d’usage répond à une intention formulée par un acteur : « confirmer la commande 742 », « affecter ce dossier à une agence » ou « annuler l’expédition avant enlèvement ». Il charge les informations utiles, appelle le domaine, persiste le nouvel état et produit un résultat compréhensible par l’appelant. Sa frontière donne un nom à l’action et à ses erreurs attendues.

La coordination commence lorsque plusieurs participants autonomes doivent contribuer : stock, paiement, facturation, transport ou outil tiers. Un orchestrateur garde alors l’état du processus, décide l’étape suivante et traite les réponses tardives. Il ne devrait pas recalculer les règles de prix ni décider si une remise est valide ; sinon la même règle finit par diverger entre le domaine et le processus.

Une service layer, enfin, est une façade applicative. Elle peut exposer les cas d’usage à plusieurs entrées — API, commande planifiée, interface d’administration — et appliquer des préoccupations transverses cohérentes. Si le projet ne possède qu’un canal et quelques actions, cette façade peut se confondre avec les cas d’usage. La distinction reste conceptuelle avant d’être physique.

Repérer une couche de services devenue fourre-tout

Le premier symptôme est une dépendance croissante : le service reçoit un dépôt pour chaque table, quatre clients HTTP, un bus, un moteur de gabarits et le contexte de sécurité. Ajouter une option commerciale oblige alors à comprendre l’expédition et la notification. Ce couplage indique que plusieurs rythmes de changement ont été réunis dans la même unité.

Le deuxième symptôme est un contrat pauvre. Une méthode reçoit un tableau libre, renvoie un booléen et lève une exception générique. L’appelant ne distingue pas un refus métier, une donnée absente, une dépendance indisponible ou un traitement déjà effectué. Cette ambiguïté migre jusqu’à l’interface, puis vers le support qui ne peut proposer aucune action fiable.

Le troisième symptôme apparaît dans la transaction. Certains appels réseau sont réalisés avant le commit, d’autres après, sans choix documenté. Un timeout laisse donc un état inconnu : le fournisseur a peut-être accepté l’ordre alors que la base locale l’a annulé. Le signal faible est souvent un script interne de « remise en cohérence » que seule une personne ose lancer.

Pour objectiver le diagnostic, prenez les dix derniers changements de la classe. Si plus de la moitié modifient des responsabilités différentes, si un test nominal exige plus de cinq doublures ou si aucun journal ne permet de localiser une étape échouée, une séparation mérite d’être étudiée. Ces nombres sont des seuils de travail locaux, pas des normes universelles.

Donner une responsabilité nette à la service layer

Une service layer stable offre un catalogue de capacités explicites. Ses méthodes parlent le langage du produit, acceptent des commandes typées et retournent des résultats documentés. Elle définit aussi les règles communes d’entrée : authentification applicative, validation de forme, corrélation et traduction des erreurs vers le canal concerné.

Elle ne doit pas devenir une seconde couche métier. Une règle telle que « un avoir supérieur au solde disponible est refusé » appartient à l’objet ou au service de domaine qui possède le solde. La façade appelle cette règle et traduit son refus ; elle ne la recopie pas dans une condition pour éviter un appel. Cette discipline permet d’exécuter le même comportement depuis l’API et un traitement planifié.

Elle ne doit pas non plus masquer toutes les dépendances derrière une méthode universelle. Les contrats techniques utiles restent visibles au constructeur : horloge, stockage, publication et identité de l’acteur. En revanche, les détails de protocole restent derrière des ports nommés selon le besoin, par exemple réserver un stock ou demander une autorisation de paiement.

Décision pratique. Conservez une façade séparée lorsque plusieurs canaux partagent réellement les mêmes capacités ou qu’une politique transverse doit rester uniforme. Dans une application plus petite, laissez le contrôleur appeler directement un cas d’usage typé. Ajouter une service layer uniquement pour respecter un dessin d’architecture augmente le nombre de sauts sans réduire le risque.

Écrire un cas d’usage autour d’une intention observable

Un bon cas d’usage commence par un verbe et se termine par un résultat que le métier reconnaît. « Mettre à jour la commande » est trop large ; « confirmer une commande préparée » expose l’état initial, l’intention et la transition attendue. La commande d’entrée contient l’identifiant, la version connue et les choix de l’acteur, sans transporter un objet ORM modifiable.

Le déroulé reste court : charger l’agrégat, vérifier l’autorisation contextuelle, demander la transition, enregistrer, puis publier les faits nécessaires. Le cas d’usage fixe la frontière transactionnelle locale. Les effets externes qui ne peuvent pas participer au commit sont inscrits dans une boîte d’envoi ou confiés à un processus explicite.

Le résultat distingue les issues normales. Une confirmation déjà réalisée peut être un succès idempotent ; une commande annulée constitue un refus métier ; une version obsolète demande une nouvelle lecture. Cette précision évite qu’une exception technique devienne un message « une erreur est survenue » pour tous les cas.

Un cas d’usage n’est pas nécessairement une classe par bouton. Deux interfaces qui déclenchent la même intention peuvent partager la même opération. À l’inverse, deux actions qui écrivent la même table ne constituent pas forcément le même cas : corriger une adresse avant expédition et forcer une adresse après incident n’ont ni les mêmes droits, ni les mêmes traces.

Réserver l’orchestrateur aux processus distribués

L’orchestrateur devient pertinent lorsque le processus survit à une requête et attend des réponses indépendantes. Il conserve un identifiant de corrélation, l’étape courante, les messages déjà traités et les échéances. Sa mission est de faire avancer le processus ou de l’amener vers un état terminal explicable.

Chaque étape doit être idempotente. Recevoir deux fois la confirmation de paiement ne doit pas créer deux expéditions. Le stockage associe donc l’identifiant du message, la version du processus et le résultat appliqué. Une contrainte unique apporte souvent une meilleure garantie qu’un simple test applicatif exécuté avant l’écriture.

Les compensations sont des décisions produit, pas des annulations magiques. Si le stock a été réservé mais que le paiement échoue, libérer la réservation est possible. Si un billet nominatif a déjà été émis, l’annulation peut exiger un avoir et une intervention. Le processus doit nommer ces états au lieu de prétendre restaurer toujours le monde initial.

Contre-intuitivement, une orchestration explicite n’est pas toujours préférable à la chorégraphie d’événements. Pour trois réactions indépendantes sans ordre métier, publier un fait puis laisser chaque consommateur agir reste plus simple. L’orchestrateur devient utile lorsque l’ordre, l’échéance, la compensation ou le verdict global doivent être connus et pilotés.

Choisir une frontière transactionnelle explicite

Une transaction locale protège un invariant dans un stockage maîtrisé. Elle doit être aussi courte que possible, mais assez large pour empêcher un état invalide. L’appel à un service distant ne doit généralement pas rester ouvert dans cette transaction : sa latence augmente les verrous et son résultat ne peut pas être annulé par le moteur de base de données.

La boîte d’envoi transactionnelle résout une partie du problème. Le cas d’usage écrit l’état métier et le message à publier dans le même commit ; un relais envoie ensuite le message et marque sa progression. Le consommateur accepte les doublons grâce à l’idempotence. Cette construction offre une livraison au moins une fois, pas une exécution exactement une fois.

Le seuil d’alerte dépend du produit. Une équipe peut examiner tout message bloqué depuis dix minutes sur un parcours immédiat, mais accepter quatre heures pour un export nocturne. Elle peut suspendre une extension si plus de 0,5 % des processus nécessitent une reprise manuelle sur deux semaines. Ces seuils doivent être validés avec les volumes et le coût d’erreur réels.

Documentez aussi le retour arrière : désactiver la nouvelle route, laisser finir les processus engagés, réconcilier les états, puis revenir à l’ancien traitement. Un déploiement réversible ne signifie pas qu’un processus distribué déjà lancé peut être effacé. La procédure doit différencier code, données et engagements externes.

Découper un traitement de commande sans ambiguïté

Cas concret : une commande B2B soumise à un plafond de crédit

Un commercial confirme une commande de 18 000 euros. Le cas d’usage charge la commande et le compte client, vérifie que les lignes sont encore modifiables, calcule le total par le domaine et constate qu’une autorisation crédit est nécessaire. Il enregistre l’état « en attente de crédit » et un événement dans la boîte d’envoi, dans la même transaction.

L’orchestrateur reçoit cet événement, demande la décision au système financier et attend au maximum deux heures ouvrées. Une acceptation déclenche la réservation de stock ; un refus ferme le processus avec un motif visible ; une absence de réponse crée une tâche pour l’équipe crédit. Le cas d’usage initial ne reste pas bloqué et l’utilisateur voit immédiatement le statut exact.

Supposons que le service financier accepte, mais que la réponse soit livrée deux fois. La clé composée du processus et du message empêche la seconde transition. Si la réservation échoue ensuite, l’orchestrateur ne modifie pas la règle de crédit : il place la commande en attente d’approvisionnement et applique l’échéance définie par le produit.

La service layer expose enfin la consultation et les actions autorisées aux différents canaux. Elle traduit « version obsolète » en réponse de conflit pour l’API et en invitation à recharger pour le back-office. Chaque niveau apporte ainsi une responsabilité distincte, vérifiable sans reconstituer toute la chaîne.

Tester chaque niveau avec la bonne preuve

Les règles de domaine se testent sans infrastructure : état initial, décision et nouvel état. Les cas d’usage se testent avec des ports contrôlés et vérifient la transaction, les erreurs typées et les faits produits. Les adaptateurs disposent de tests d’intégration contre la vraie base ou un environnement contractuel du fournisseur.

L’orchestrateur exige des scénarios temporels. Testez une réponse en retard, un doublon, une réponse dans le désordre, une dépendance indisponible et une reprise après redémarrage. L’horloge doit être injectable afin de franchir une échéance sans attendre réellement. Le scénario affirme l’état du processus et la prochaine action, pas seulement le nombre d’appels.

Un test de bout en bout reste nécessaire pour quelques parcours critiques, mais il ne remplace pas ces preuves ciblées. S’il échoue, l’équipe doit localiser le niveau fautif sans lire tous les journaux. Une pyramide composée uniquement de tests unitaires avec des doublures peut masquer un contrat SQL ou HTTP incompatible.

Avant la mise en production, rejouez au moins un échec après chaque effet externe et exécutez la procédure de reprise avec un compte d’exploitation. Le critère de réussite est qu’une personne qui n’a pas développé la fonctionnalité puisse identifier le dossier, comprendre son état et choisir une action sûre.

Instrumenter la coordination pour le support

Chaque intention reçoit un identifiant de corrélation propagé aux messages et appels sortants. Les journaux structurés indiquent le cas d’usage, l’identifiant métier, l’étape, la version et l’issue, sans exposer de donnée sensible. Une chronologie peut alors être reconstruite sans concaténer des recherches approximatives.

Les métriques utiles décrivent le produit : durée par étape, processus en attente, taux de compensation, âge du plus ancien message et reprises manuelles. Une moyenne globale dissimule les dossiers bloqués ; les distributions et les files par état donnent une vue plus exploitable. Chaque alerte indique une responsabilité et une action attendue.

Prévoyez un écran de support limité aux gestes sûrs : consulter la chronologie, relancer une étape idempotente, annuler selon une compensation autorisée ou escalader. Modifier directement un statut en base détruit les invariants et la preuve. Les opérations sensibles demandent un motif et produisent une trace d’audit.

Le guide d’observabilité des workflows métier approfondit cette continuité entre événement, indicateur et action de support. L’objectif n’est pas d’accumuler des tableaux, mais de réduire le temps entre le signal et une décision sûre.

Pour qui cette séparation devient prioritaire

La méthode concerne d’abord les équipes qui exploitent des parcours avec plusieurs écritures, des appels tiers ou des traitements asynchrones. Elle aide aussi un responsable produit lorsque les utilisateurs ne savent pas si une action est terminée et que le support dépend d’une lecture technique.

Pour un simple formulaire éditorial enregistré dans une base unique, un contrôleur mince et un service applicatif suffisent souvent. La séparation devient prioritaire quand un échec partiel crée un engagement financier, un stock incohérent, une action irréversible ou une reprise manuelle fréquente.

Le responsable technique porte la frontière et les contrats ; le produit définit les états et compensations acceptables ; l’exploitation valide les diagnostics et les actions de reprise. Aucun de ces rôles ne peut concevoir seul un processus fiable, car la cohérence technique ne décide pas du sens métier d’une compensation.

Erreurs fréquentes et contre-mesures

Créer une classe par notion sans critère de sortie

Multiplier interfaces, gestionnaires et façades ne crée pas une architecture. Chaque élément doit protéger une décision ou isoler une dépendance changeante. Si deux niveaux portent les mêmes données, les mêmes règles et les mêmes tests, fusionnez-les jusqu’à ce qu’un besoin réel impose leur séparation.

Cacher un processus long dans une requête synchrone

Augmenter le timeout ne règle ni l’état inconnu ni la reprise. Dès qu’un participant distant peut répondre après la requête, donnez un statut au processus et rendez sa progression consultable. Le canal peut attendre brièvement une réponse rapide, puis basculer vers ce suivi explicite.

Confondre erreur technique et refus métier

Un plafond dépassé, un timeout et une version concurrente n’appellent pas la même réponse. Définissez un vocabulaire d’issues et conservez la cause technique séparément. Le support peut ainsi conseiller l’utilisateur sans traduire une pile d’exception.

Automatiser la reprise avant de la comprendre

Une relance automatique d’une opération non idempotente amplifie l’incident. Commencez par une reprise manuelle tracée sur quelques cas, observez les décisions, puis automatisez uniquement le geste stable. La priorité reste d’empêcher un double effet, pas de faire disparaître toute intervention humaine.

Décider sans ajouter une couche artificielle

Bloc de décision. Gardez un seul cas d’usage lorsque toutes les étapes partagent la même base, terminent en quelques secondes et peuvent être annulées par le même commit. Ajoutez une service layer distincte si plusieurs canaux partagent une politique d’entrée. Introduisez un orchestrateur persistant si le processus attend, coordonne plusieurs autorités ou exige une compensation.

Différez la séparation si l’équipe ne peut pas encore nommer les états métier et les responsabilités. Refactoriser des classes avant de comprendre le processus déplace seulement la confusion. Dans ce cas, instrumentez d’abord le traitement actuel, échantillonnez les incidents et dessinez la chronologie de trois dossiers réels.

Refusez une extension de périmètre si aucun mécanisme ne prévient les doublons, si la source de vérité reste disputée ou si le retour arrière dépend d’une modification directe en base. Ces conditions d’arrêt protègent l’activité plus efficacement qu’une couverture de tests élevée sur le seul chemin nominal.

Le coût complet de la solution inclut conception, migration, supervision, astreinte, reprise et formation du support. Une architecture légèrement plus longue à développer peut réduire le coût du run ; l’inverse est également vrai si elle impose une infrastructure distribuée à un processus local et stable.

Plan d’action sur deux itérations

Première itération : rendre le comportement visible

  1. Sélectionnez un parcours douloureux et décrivez cinq dossiers réels, dont un doublon, un timeout et un refus métier.
  2. Nommez l’intention, les invariants, les participants, la source de vérité et l’état visible après chaque étape.
  3. Ajoutez corrélation, journaux structurés et métriques de file sans modifier encore l’enchaînement fonctionnel.
  4. Écrivez la procédure manuelle de diagnostic et de reprise, puis faites-la exécuter par une personne du support.

À la fin de cette itération, l’équipe doit expliquer les échecs observés et mesurer leur fréquence. Elle fixe un seuil local : par exemple aucun double effet, moins de trente minutes pour diagnostiquer un dossier critique et moins de 1 % de reprises manuelles. Le produit valide ces limites selon la valeur et le risque, sans les transformer en standard universel.

La fiche d’exécution décrit les entrées, les sorties, les dépendances, les seuils et la journalisation attendue pour chaque transition. Elle relie ainsi le contrat du cas d’usage à une preuve que le support sait retrouver, sans l’obliger à lire le code ou à interroger directement les tables.

Deuxième itération : séparer et éprouver la reprise

  1. Extrayez la règle métier la plus stable, puis donnez au cas d’usage une commande et des résultats typés.
  2. Placez les effets externes après une boîte d’envoi et rendez les consommateurs idempotents avec une contrainte persistante.
  3. Créez un orchestrateur seulement pour les étapes qui attendent ou compensent, avec une version et des états explicites.
  4. Déployez derrière un indicateur d’activation, comparez l’ancien et le nouveau parcours, provoquez un échec, puis exécutez le retour arrière documenté.

La gouvernance tient dans une revue hebdomadaire courte : produit pour les refus et compensations, technique pour les contrats et exploitation pour les reprises. L’extension n’est autorisée que si les dossiers sont retrouvables, si les doublons sont absorbés et si le support utilise la procédure sans aide orale. Sinon, l’équipe réduit le périmètre et corrige la frontière la plus coûteuse.

Les responsabilités sont consignées avant le déploiement : la technique suit l’instrumentation, l’exploitation exécute le rollback et le produit tranche les compensations. Un exercice sur une dépendance coupée confirme que ce partage tient avec les droits réels, puis son résultat conditionne l’ouverture du trafic suivant.

Guides complémentaires pour fiabiliser l’architecture

Éprouver les scénarios et les exceptions

Le guide consacré à la vérification des workflows métier et de leurs exceptions aide à construire les cas de désordre, de répétition et de reprise qui révèlent une mauvaise frontière.

Faire évoluer le socle sans casser le service

La méthode de migration Symfony sans interruption du run complète cette démarche lorsque la séparation accompagne une évolution de framework ou d’infrastructure.

Ces lectures doivent rester reliées au cas traité. Un processus local n’a pas besoin d’une plateforme événementielle pour bénéficier de commandes typées, de transactions courtes et de diagnostics cohérents.

Conclusion : des frontières lisibles jusque dans le run

Séparer service layer, cas d’usage et orchestrateur ne consiste pas à distribuer du code entre trois dossiers. La séparation devient valable lorsqu’une intention métier, une transaction locale et un processus long possèdent chacun un contrat et une preuve adaptés.

Le cas d’usage protège la cohérence immédiate ; l’orchestrateur rend l’attente, les doublons et les compensations explicites ; la service layer offre une entrée stable lorsque plusieurs canaux en ont réellement besoin. Le domaine conserve les décisions qui ne doivent pas varier selon le protocole.

Commencez par instrumenter un parcours, définir ses états et faire rejouer un incident. Vous pourrez ensuite refactoriser avec un seuil, un responsable et un chemin de retour, sans transformer l’architecture cible en pari irréversible.

Pour cadrer ces frontières dans une trajectoire de développement web sur mesure, Dawap peut auditer un flux critique, concevoir ses contrats et accompagner une première mise en œuvre vérifiée avec les équipes produit et exploitation.

Portrait de Jérémy Chomel

Vous avez un projet de
développement sur mesure ?

Dawap transforme ce besoin en périmètre livrable, architecture maintenable et trajectoire de mise en production adaptée à vos contraintes.

Besoin d’échanger sur votre projet ? Planifier un rendez-vous

Articles recommandés

Observabilité fonctionnelle d’un workflow métier de bout en bout Développement web Observabilité d’un workflow métier : voir le dossier réel Lire l'article
  • 17 juillet 2026
  • Lecture ~17 min

Logs techniques et disponibilité ne suffisent pas. Instrumentez états, transitions, décisions, délais et reprises pour expliquer où un dossier métier s’est réellement bloqué. Le guide relie événements fonctionnels, traces, métriques, alertes et modes opératoires sans transformer les données personnelles en identifiants de corrélation.

Stratégie de test d’un workflow métier à nombreuses exceptions Développement web Tester un workflow complexe sans explosion combinatoire Lire l'article
  • 17 juillet 2026
  • Lecture ~17 min

Testez les états, transitions, invariants, droits, données et reprises qui portent le risque réel, au lieu de multiplier des scénarios impossibles à maintenir. Cette méthode construit une couverture défendable, injecte les pannes utiles et vérifie aussi les compensations, la concurrence et les preuves attendues par le métier.

Migration progressive d’une application Symfony sans interruption du run Développement web Migration Symfony : monter de version sans casser le run Lire l'article
  • 16 juillet 2026
  • Lecture ~14 min

Une montée de version Symfony touche PHP, dépendances, configuration, données, sessions, cache, Messenger, crons et contrats API. Ce guide propose une trajectoire progressive, une baseline de tests, des critères de retour arrière et une matrice go ou no-go pour moderniser l’application sans confondre migration du framework et refonte métier.

Performance et monitoring d’une application métier Développement web Performance et monitoring d’une application métier Lire l'article
  • 20 janvier 2025
  • Lecture ~45 min

La performance d’une application métier se juge sur la tâche accomplie, pas sur une moyenne globale. Reliez latence, erreurs, saturation et signaux métier, puis définissez les alertes qui déclenchent une action. Traces, métriques et journaux deviennent alors un outil de diagnostic, de dégradation maîtrisée et de reprise.