5  Diagrammes de séquence système (DSS)

Un diagramme de séquence système (DSS) est un diagramme UML (diagramme de séquence) limité à un acteur (provenant du scénario d’un cas d’utilisation) et au Système. Le DSS décrit l’interaction entre l’acteur et le système, sans rentrer dans les détails du système (une modélisation appelée « boîte noire » parce que les détails sont obscures). La modélisation à ce niveau permet de définir plus tard les détails à l’intérieur du système.

5.1 L’interface de programmation (Application Programming Interface)

Selon l’Office québécois de la langue française, l’interface de programmation est l’ensemble de routines standards, accessibles et documentées, qui sont destinées à faciliter au programmeur [et à la programmeuse] le développement d’applications. Le terme en anglais est Application Programming Interface (API). En d’autres mots, c’est la façon (normalisée et documentée) dont un programme d’application communique avec un ensemble d’éléments logiciels qu’il utilise pour faire une application utile.

Par exemple, une classe dans un logiciel orienté objet peut être utilisée pour créer une application. Les méthodes publiques de la classe composent son interface API, comme le montre la figure 5.1. Un logiciel en ligne de commande peut aussi servir pour des applications. Les commandes et les paramètres que le logiciel accepte forment son interface API. Une commande sous Linux peut avoir une interface API, comme il est illustré dans l’exemple de la figure 5.2. Un service REST  est une façon de créer une application Web. On communique avec le service REST à l’aide de requêtes HTTP. La requête HTTP contient l’URI vers la routine à accéder, la méthode de requête (verbe HTTP) et les paramètres de la routine, tel qu’illustré sur la figure 5.3.

Les interfaces humain-machine (IHM), dont les interfaces graphiques, ne sont pas une interface API puisqu’elles ne peuvent être utilisées de manière logicielle. Elles permettent de faire le pont entre les utilisateurs et utilisatrices (qui communiquent par les gestes, par exemple un clic avec une souris) et l’interface API du logiciel afin d’en faciliter l’utilisation.

PointgetX() : numbersetX(x : number)getY() : numbersetY(y : number)distance(point : Point) : numberdistance(point1 : Point, point2 : Point) : number

Figure 5.1: Les méthodes publiques d’une classe sont un exemple d’interface API. (PlantUML)

$ pwd --help
pwd: pwd [-LPW]
    Print the name of the current working directory.

    Options:
      -L        print the value of $PWD if it names the current working directory
      -P        print the physical directory, without any symbolic links
      -W        print the Win32 value of the physical directory

Figure 5.2: L’interface API de la commande pwd sous Linux.

GET https://service.com/api/v1/employee

GET https://service.com/api/v1/employee/{id}

POST https://service.com/api/v1/employee
{
  "name": "John Doe",
  "rate": 20
}

Figure 5.3: Exemple d’interface API d’un service REST.

5.2 La séparation des couches présentation et domaine

Une des forces du DSS est qu’il fait abstraction de beaucoup de détails. Cependant, il sera nécessaire de les comprendre, car certaines règles s’imposent dans la méthodologie et on veut préparer le terrain pour expliquer pourquoi ces règles existent. En plus, dans la partie de la méthodologie où on va rentrer dans les détails (les réalisations de cas d’utilisation), il faudra maîtriser le principe de la séparation des couches présentation et domaine. Commençons par une explication de base de ce principe.

Puisqu’il y a des acteurs humains interagissant avec le système, on doit avoir une IHM dans le système. Avec l’évolution rapide des technologies, il y a toujours eu un potentiel de concevoir des IHM de plus en plus conviviales et efficientes. Par exemple, les premières versions de Microsoft Word tournaient sur MS-DOS (PC d’IBM) au milieu des années 1980 (la souris n’était même pas nécessaire pour l’utiliser). Pourtant, le concept de style nommé (gabarit) pour les paragraphes existait, comme on le trouve dans la version de Word d’aujourd’hui.

Les développeurs et développeuses ont appris qu’au fil du temps, les parties du code liées à l’IHM (les menus, les boutons, l’animation, etc.) sont relativement instables par rapport aux parties liées au domaine du problème (le concept de style de paragraphe). Aujourd’hui, des applications populaires sont déployées sur les plateformes mobiles, PC et Web (infonuagiques). La reconnaissance automatique de la parole  et les dispositifs haptiques  sont des exemples plus modernes de technologies qui permettent en principe une meilleure IHM.

Donc, pour minimiser l’impact des changements de l’IHM sur tout un système, on peut comprendre l’intérêt d’isoler la partie « domaine » (qui est relativement stable) de la partie IHM (qui est différente selon la plateforme et qui a une tendance à évoluer à cause des nouvelles technologies). La conception de ces systèmes comprend un aspect important qui est la séparation des couches, notamment celle de l’IHM qu’on nomme la couche présentation et celle qui contient la logique d’affaires qu’on nomme la couche domaine. Il y a d’autres bénéfices de cette séparation, mais elle impose certaines règles qui vont à l’encontre des habitudes des développeurs et développeuses qui aiment coder rapidement.

Sans séparation des élémentsBeaucoup de couplageentre éléments de l'IHMet du domaine.IHMIHMIHMD1D2D3D4D5D6

(a) Sans séparation, les éléments de l’IHM sont fortement couplés aux éléments de la logique de l’application. Ce genre de solution peut être créé rapidement. Cependant, si le code de l’IHM doit changer pour accommoder une nouvelle technologie, ces changements peuvent affecter le reste du système. (PlantUML)

Bonne séparation entre couchesCouche présentationReste du système (couche domaine)Pattern Façade (GoF)réduit le couplageet favorise la séparationdes couches.IHMIHMIHMD1D2D3D4D5D6ContrôleurGRASPContrôleurGRASPopérations système(définies auDSS)

(b) La définition et la séparation des couches présentation et domaine permettent de minimiser l’impact des changements dus aux évolutions dans les technologies liées à l’IHM et d’avoir une conception plus cohésive. Cependant, cette séparation requiert un respect de l’architecture par les développeurs et développeuses. (PlantUML)

Figure 5.4: Raison d’être de la séparation des couches présentation et domaine.

Le DSS vise à modéliser l’interface API du système développé, et ce, peu importe la façon dont les utilisateurs et les utilisatrices vont interagir avec celui-ci. Lorsqu’on crée un DSS dans la méthodologie de ce manuel, on propose une conception de haut niveau qui fait un lien direct entre les besoins (les étapes d’un cas d’utilisation) et une solution qui répond à ces besoins, sans aller trop en détail (on fera cela plus tard, justement dans les RDCU), tout en respectant la séparation des couches présentation et domaine.

Voici d’autres avantages de la séparation des couches présentation et domaine :

  • Une forte cohésion des éléments de chaque couche, grâce à la séparation des préoccupations .
  • Un faible couplage entre la couche présentation et la couche domaine.
  • La couche domaine est « protégée » des évolutions du code dans la couche présentation.
  • Il peut y avoir plusieurs couches de présentation (site web, app mobile) pour la même couche domaine.
  • L’interface API de la couche domaine peut être utilisé par des systèmes externes.
  • Les classes cohésives peuvent être plus facilement réutilisées dans un autre projet.
  • Les classes cohésives sont plus faciles à tester.
  • Une séparation peut rendre l’application plus simple à comprendre (pour les nouvelles personnes dans l’équipe de développement).

Finalement, l’architecture modèle-vue-contrôleur ou MVC  existe depuis 1978, et la séparation des couches en est un principe fondamental.

5.3 Les DSS en tant qu’artefact

Les DSS sont expliqués en détail dans le chapitre 10 , mais voici des points importants pour la méthodologie de ce manuel :

  • Le DSS a toujours un titre.
  • L’acteur est indiqué dans la notation par un bonhomme et est représenté comme une instance de la classe du bonhomme, comme :Joueur sur la figure 5.5 (le « : » signifie une instance).
  • Le Système est un objet (une instance :Système) et n’est jamais détaillé plus.
  • Le but du DSS est de définir des opérations système, qui deviendront l’interface API du système ; il s’agit d’une conception de haut niveau.
  • Le côté acteur du DSS n’est pas un acteur tout seul, mais une couche logicielle de présentation, comme une interface graphique ou un logiciel qui peut reconnaître la parole. Cette couche reconnaît des gestes de l’acteur et envoie une opération système. Un geste peut être un clic sur un bouton dans l’interface, une demande « Dis Siri », etc.
  • Puisque la couche présentation reçoit des informations des êtres humains, les opérations système ont des arguments de type primitif. Il est difficile pour un utilisateur de spécifier une référence (pointeur en mémoire) à un objet. Alors, on peut donner le nom (de type String) d’un morceau de musique à jouer, ou spécifier une quantité (de type Integer).
  • Puisque les types des arguments sont importants, on les spécifie dans les opérations système du DSS.
  • Un message de retour (ligne pointillée avec flèche ouverte) vers l’acteur représente la communication des informations précises, par exemple les valeurs des dés dans l’attaque du jeu Risk. Puisque la couche présentation a beaucoup de moyens pour afficher ces informations, on ne va pas spécifier les messages de retour comme des méthodes.

5.4 Exemple : DSS pour Attaquer un pays

La figure 5.5 est un exemple de DSS pour le cas d’utilisation Attaquer un pays. Dans cet exemple, il y a quatre opérations système (avec les arguments de type primitif, sauf la dernière qui n’a aucun argument) et un message de retour. Vous pouvez noter tous les détails (titre, arguments, types).

:Joueur:SystèmedémarrerAttaque(paysAttaquant : String,paysDéfenseur : String)loop[pas terminé]annoncerAttaque(nbRégimentsAttaquant : int)annoncerDéfense(nbRégimentsDéfenseur : int)résultats des deux lancers, régiments perdusde l'attaquant et du défenseur le cas échéantterminerAttaque()

Figure 5.5: Diagramme de séquence système pour le scénario Attaquer un pays. (PlantUML)

5.5 Les DSS font abstraction de la couche présentation

Le but du DSS est de se concentrer sur l’interface API (les opérations système) de la solution. Dans ce sens, c’est une conception de haut niveau. Le « Système » est modélisé comme une boîte noire. Par exemple, sur la figure 5.6, il y a l’acteur, le Système et une opération système. On ne rentre pas dans les détails, bien qu’ils existent et soient importants.

:Joueur:SystèmedémarrerAttaque(...)

Figure 5.6: Une opération système dans un DSS. C’est une abstraction. (PlantUML)

Plus tard, lorsque c’est le moment d’implémenter le code, les détails importants devront être respectés. Il faut faire attention aux principes de la séparation des couches présentation et domaine. Par exemple, la figure 5.7 rentre dans les détails de ce qui se passe réellement dans une opération système quand la solution fonctionne avec un service Web :

  • D’abord, l’acteur clique sur un bouton ;
  • Ce clic se transforme en appel REST ;
  • Un routeur transforme l’appel REST en une opération système envoyée à un contrôleur GRASP. Notez que c’est un objet du domaine qui reçoit l’opération système – c’est l’essence du principe GRASP Contrôleur ;
  • Le contrôleur GRASP dirige la suite, selon la solution proposée dans la réalisation de cas d’utilisation (RDCU).

Couche présentationCouche domaine:Joueur« NavigateurWeb »:Button« NodeExpress »:Routeur« ContrôleurGRASP »:JeuRisk...cliquerHTTP GET/api/v1/démarrerAttaque/...Router handler (Express)démarrerAttaqueOpération système définie dans DSSdémarrerAttaque(...)...selon la RDCU

Figure 5.7: Une opération système est envoyée par la couche présentation et elle est reçue dans la couche domaine par son contrôleur GRASP. Ceci est un exemple avec un navigateur Web, mais d’autres possibilités existent pour la couche présentation. (PlantUML)

La figure 5.7 est à titre d’information seulement. Un DSS ne rentre pas dans tous ces détails.

5.6 FAQ DSS

5.6.1 Faut-il une opération système après une boucle ?

Dans l’exemple pour Attaquer un pays (figure 5.5), à l’extérieur de la boucle, il y a une opération système terminerAttaque. Est-ce obligatoire d’avoir une opération système après une boucle ?

L’opération système terminerAttaque sert à signaler la fin de la boucle. Le système saura que l’acteur ne veut plus répéter les actions dans la boucle. Mais elle permet aussi de faire des calculs concernant ce qui s’est passé dans la boucle, par exemple pour déterminer qui contrôle quel pays après les attaques.

Cependant, si vous avez une boucle pour indiquer la possibilité de répéter une action (par exemple ajouter des produits dans un système d’inventaire) et que vous n’avez pas besoin de faire un calcul à la fin, alors une opération système pour terminer une telle boucle n’est pas nécessaire (surtout avec une application Web).

5.6.2 Comment faire si un cas d’utilisation a des scénarios alternatifs ?

Fait-on plusieurs DSS (un pour chaque scénario alternatif) ou utilise-t-on la notation UML (des blocs opt et alt) pour montrer des flots différents dans le même DSS ?

Un objectif derrière le DSS est de définir les opérations système. Donc, on peut se poser la question suivante : les scénarios alternatifs impliquent-ils une ou plusieurs opérations système n’ayant pas encore été définies ? Si la réponse est non, on peut ignorer les scénarios alternatifs dans le DSS. Par contre, si la réponse est oui, il est essentiel de définir ces opérations système dans un DSS.

Quant au choix de faire des DSS séparés ou d’utiliser la notation UML pour montrer les flots différents dans le même DSS, ça dépend de la complexité de la logique des flots. Un DSS devrait être facile à comprendre. C’est à vous de juger si votre DSS avec des opt ou des alt est assez simple ou est compliqué à lire. Utilisez un autre DSS (ou plusieurs) ayant le nom des scénarios alternatifs si cela vous semble plus clair.

5.6.3 Est-ce un bon design d’avoir une opération système avec beaucoup d’arguments (de type primitif) ?

Selon la méthodologie, une opération système doit avoir seulement des arguments de type primitif. À cause de ça, j’ai plusieurs opérations système avec de nombreux (plus de cinq) arguments. Par exemple, pour créer un Devoir (dans le projet du laboratoire), il y a beaucoup d’informations provenant du formulaire Web et on doit les passer toutes à l’opération système. Pourquoi n’est-il pas permis d’instancier un objet de Devoir d’abord et le passer comme un seul argument ?

Il y a deux volets à cette question.

  1. Il y a le problème de beaucoup d’arguments (de type primitif) pour une opération système. Cela peut arriver, surtout pour les opérations sur les objets du domaine (comme le Devoir) qui ont beaucoup d’attributs. En effet, il n’est pas convivial d’avoir beaucoup d’arguments dans un appel (une opération).

  2. Ensuite, il n’est pas conseillé de passer des objets du domaine (par exemple, le Devoir) comme arguments, puisque c’est la couche présentation qui invoque l’opération système. La couche présentation n’est pas censée manipuler directement les objets dans la couche domaine, sinon elle empiète sur les responsabilités de la couche domaine.

Une solution pour réduire le nombre d’arguments sans utiliser un objet du domaine est d’appliquer un réusinage pour le smell nommé Long Parameter List, par exemple Introduce Parameter Object. Notez que l’objet de paramètre que vous introduisez n’est pas un objet (classe) du domaine ! La distinction est importante, car la logique d’affaires demeure dans la couche domaine. En TypeScript, une fonction peut être définie avec un objet de paramètre. Cet exemple montre même comment on peut « déstructurer » l’objet pour déclarer les variables utilisées dans la fonction :

// La fonction n'a qu'un seul argument (un objet de paramètre).
// Exemple inspiré de https://leanpub.com/essentialtypescript/read#destructuring
function compteARebours({ initial: number, final: final = 0,
                          increment: increment = 1, initial: actuel }) {
    while (actuel >= final) {
        console.log(actuel);
        actuel -= increment
    }
}
compteARebours({ initial: 20 });  // on passe un objet de paramètre
compteARebours({ initial: 20, increment: 2, final: 4 });

Passer un objet de paramètre à une opération système respecte la séparation des couches (la modularité) et augmente la lisibilité du code.

5.6.4 Ne serait-il pas plus simple de passer l’objet body de la page Web au contrôleur GRASP ?

Décortiquer toutes les informations dans un formulaire Web est compliqué, puis on doit passer tout ça à un contrôleur GRASP comme des arguments de type primitif. Ne serait-il pas plus simple de passer l’objet body de la page Web au contrôleur GRASP et de le laisser faire le décorticage ?

Dans un sens, ça serait plus simple (pour le code de la couche présentation). Cependant, on veut séparer les couches pour favoriser le remplacement de la couche présentation, par exemple à travers une application iOS ou Android.

Si vous mettez la logique de la couche présentation (décortiquer un formulaire Web) dans la couche domaine (le contrôleur GRASP), ça ne respecte pas les responsabilités des couches. Imaginez un tel contrôleur GRASP si vous aviez trois types d’applications frontales (navigateur Web, application iOS et application Android). Le contrôleur GRASP recevra des représentations de « formulaire » de chaque couche présentation différente. En passant, l’objet body n’a rien à voir avec une interface Android ! Ce pauvre contrôleur serait alors obligé de connaître toutes les trois formes (Web, iOS, Android) et, ainsi, sa cohésion serait beaucoup plus faible. Pour respecter les responsabilités, on laisse la couche présentation faire le décorticage et construire une opération système selon l’interface API définie dans le DSS. Cela simplifie aussi le contrôleur GRASP.

5.7 Exercices

Note

Vous pouvez dessiner les diagrammes à la main et en prendre une photo avec une application comme Microsoft Lens (Android, iOS).

Vous pouvez également utiliser PlantUML. Voici des ressources à ce propos :

Méfiez-vous des outils comme Lucidchart ayant seulement des profils superficiels (sans règles) pour l’UML. Voir la figure 13.5 pour plus de détails.

Exercice 5.1 (Dessiner un DSS à partir d’un cas d’utilisation) Esquissez le DSS pour le cas d’utilisation Réserver un livre de la bibliothèque. Toutes les opérations système doivent définir le type de chaque argument, si nécessaire.