• 8 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 3/2/22

Gérez la communication entre les couches avec une API de données

Comme vous le savez maintenant, une application monolithique pose problème parce qu'il est difficile voire impossible de :

  • la modifier ou la maintenir facilement,

  • de tester ses composants de manière individuelle.

La solution c'est donc de diviser l’application en couches séparées.

Dans ce chapitre, nous allons regarder comment fonctionne la communication entre couches.

Construisez vos couches de communication correctement

Pour commencer, regardons comment connecter les classes contrôleurs à notre couche de vue et notre couche de données.

Dans notre cas, Spring Boot établit la communication et effectue les différentes connexions pour nous. C'est une approche qui est très utilisée. Voici une partie de ClientController.java :

@RequestMapping("/clients/owe")
public String clientOwe( Model model) {
   model.addAttribute("clients", clientRepository.findByOustandingBalanceGreaterThan(0.0));
      return "clients";
}
  • Ligne 1 : Il s’agit de la connexion entre la page d’affichage (vue) et le contrôleur. Elle dit que, lorsque l’application décide d’afficher la page /clients/owe, la fonction  clientOwe()  est appelée. Le choix d’afficher cette page est décidé par la référence qui y est faite dans clients.html .

  • Ligne 3 : Il s’agit de la connexion entre le contrôleur et la couche de données. Elle dit de trouver le modèle de données du client, puis d’ajouter toutes les données client qui correspondent à la requête du repository. Il y a une séparation claire entre ce que le contrôleur a besoin de savoir au sujet d’un client et la façon dont cette information est sauvegardée. Toutes les informations de sauvegarde sont dans la classe  ClientRepository  . Ainsi, nous pouvons contrôler (ou filtrer) les clients à afficher sur cette page.

  • Ligne 4 : Une autre connexion entre le contrôleur et la vue. Celle-ci dit à Spring Boot quelle page utiliser pour afficher les données obtenues à la ligne précédente.

Si nous suivons cette approche consistant à avoir des communications claires entre les diverses couches, nous pouvons très rapidement ajouter différentes pages, avec des filtres différents. Voyons à quelle vitesse vous pouvez le faire !

À vous de jouer !

Ajoutez cette fonctionnalité pour un nouveau cas d’usage : « Trouver tous les clients qui doivent beaucoup d’argent (défini comme un montant supérieur à 150), pour que je puisse les appeler personnellement. »

Si vous avez besoin de plus d’instructions, voici les étapes :

Etape 1 : Ajoutez une méthode à la classe ClientController.

@RequestMapping("/clients/owe-lots")
public String clientOweLots( Model model) {
    model.addAttribute("clients", clientRepository.findByOustandingBalanceGreaterThan(150.0));
    return "clients";
}

Etape 2 : Maintenant, ajoutez un bouton à la page clients.html.

<a href="/clients/owe-lots" class="btn btn-primary">Owe Lots</i></a>

D’accord, ça me semble logique. Mais comment cette fonctionnalité se retrouve-t-elle en désordre ?

Elle devient désordonnée lorsque l’on oublie l’objectif de chaque couche. C'est facile d’essayer de faire faire plus de choses qu’elle ne le devrait à la couche de contrôleur. Par exemple, nous pourrions avoir fait faire son propre filtrage de clients à la méthode  clientOweLots  :

@RequestMapping("/clients/owe-lots")
public String clientOweLots( Model model) {
   List<Client> clients = new ArrayList<>();
   Iterable<Client> allClients = clientRepository.findAll();
   for (Client client: allClients) {
      if (client.getOutstandingBalance() > 150.0) {
         clients.add(client);
      }
   }

   model.addAttribute("clients", clients);
   return "clients";
}

C’est facile à court terme, mais ce sera un vrai cauchemar sur le long terme…

Concevez la communication dans une Data API

Actuellement, notre application livre toutes les données enveloppées dans un composant visuel. Mais que se passerait-il si nous devions livrer uniquement la partie données ? Nous devrions ajouter une API vers le monde extérieur.

Considérations de design pour les APIs

Il existe plusieurs questions importantes que nous devons poser lorsque nous concevons une API :

1) Quelle est l’intention de mon API ?

Tout d’abord, nous devons construire une API qui soit utile à nos utilisateurs (ceux qui appellent cette API). La solution de facilité, ça serait d’exposer toutes nos données en un ensemble d’opérations CRUD. Bien que la fonctionnalité CRUD soit utile, nous voulons une API qui expose l’intention de notre application. Comme nous l’avons vu plus tôt dans ce cours, nous devons aller plus loin que cela et fournir une fonctionnalité plus ciblée, alignée sur les besoins de notre client. Nous devons donc comprendre l’intention de notre API en revisitant nos cas d’usages.

2) Quels messages devrais-je choisir ?

Une API est définie par les messages qu’elle envoie et reçoit. Le choix des messages est donc extrêmement important. Nous explorerons un peu plus ce sujet dans la section ci-dessous.

3) De quel format de données ai-je besoin ?

Le format des messages doit être pris en compte. Il nous faut de la flexibilité. Heureusement, les messages basés sur le texte, comme le JSON ou le XML, permettent exactement cela. Ces deux options traitent les données comme paires de clé/valeur (c’est en fait un peu plus compliqué que ça, mais c’est un détail pour un autre cours).

Nous pouvons choisir l’un ou l’autre, mais le JSON est devenu un standard, c’est donc celui que nous choisirons pour notre API. Nous nous appuierons sur le framework Spring Boot, ce qui simplifiera le mécanisme de mise en place d’une API. La définition des messages sera ensuite gérée par le JSON qui sera transféré d’un côté à l’autre.

Considérations de design pour les messages dans les APIs

Prenez en compte deux domaines de réflexion lorsque vous concevez les messages d’une API.

  • Déterminez les données entrantes et sortantes : qu’est-ce qui entre, et qu’est-ce qui revient ?

  • Évaluez les modifications à venir : qu’est-ce qui changera probablement au fil du temps ?

Déterminez les données entrantes et sortantes

Pour appeler une API, nous devons d’abord savoir quelles données envoyer. Voici trois questions utiles par lesquelles commencer :

  • À quoi ressemblent les données ?

  • Quelles données sont nécessaires ? Qu’est-ce qui est facultatif ?

  • Est-ce qu’il y a des ensembles de valeurs acceptables ?

1) À quoi ressemblent les données ?

C’est facile de créer de la confusion autour de la manière dont on représente les données. Voici une situation simple qui peut créer des problèmes : la représentation des dates.

Par exemple, si la date est 2020-05-09, est-ce qu’on parle du 5 septembre, ou du 9 mai ? Nous devrons trancher d’un côté ou de l’autre (AAAA-MM-JJ ou AAAA-JJ-MM) et documenter notre décision, afin que chaque utilisateur de notre API sache ce que nous avons choisi. Le JSON n’a pas de type de date intégré, donc cette erreur est possible.

Anticipez ces types de problèmes. Si possible, utilisez des normes faisant l’unanimité (par exemple, l’échelle de temps UTC), plutôt que de choisir quelque chose au hasard.

2) Quelles données sont nécessaires ? Qu’est-ce qui est facultatif ? 

Pour illustrer ceci, nous pouvons regarder les  MaintenanceIssues  . Lorsque nous avons paramétré les données, au départ, la date d’entrée était obligatoire, mais la date de réparation était optionnelle (la réparation peut ne pas encore avoir été effectuée). Lorsque notre API prendra connaissance de ces données, nous ferons appliquer ces règles également. Le JSON fonctionne bien pour ceci. En analysant les données, nous pouvons vérifier si les valeurs nécessaires sont incluses, et rejeter les données si elles sont absentes. Si des valeurs optionnelles sont fournies, elles seront utilisées.

3) Est-ce qu’il y a des ensembles de valeurs acceptables ?

Ici, nous pouvons regarder à nouveau les  MaintenanceIssues  . Chaque problème possède un sous-système et une gravité spécifiques. Notre API ignorera les messages qui n’envoient pas le bon type de données pour ces éléments. Une fois de plus, le JSON permet ce type de comportement, étant donné que les données peuvent être stockées en tant que chaîne. La chaîne de données peut ensuite être comparée à un ensemble de valeurs valides.

Évaluez les modifications à venir

Le changement constitue une dernière considération dans la conception de notre API. Il est probable qu’avec le temps, des données différentes soient requises ou deviennent inutiles. C’est une autre raison pour laquelle le JSON fonctionne bien pour gérer les données de messages. Sa flexibilité permet d’ajouter de nouveaux éléments, et d’en supprimer des anciens. Si nous séparons cette couche qui gère les interactions utilisateur et mettons à jour nos endpoints, assurer une rétro-compatibilité est beaucoup plus facile qu’avec une conception monolithique.

Implémentez une API de données

Et maintenant, ajoutons une API à la partie maintenance de notre application. Un excellent aspect de l’utilisation de notre framework existant, c’est que nous avons très peu de choses à faire. Nous devons uniquement ajouter un contrôleur d’endpoint. Nous n’avons même pas besoin de générer une page HTML pour montrer les résultats !

Pour ajouter le nouveau contrôleur :

1. Ajoutez une nouvelle classe  MaintenanceJSONController.java  dans le paquet  com.airbusiness.airbusinessmvc.controllers .

2. Dites au framework qu’il s’agit d’un contrôleur REST, et donnez-lui l’emplacement de l’endpoint.

@RestController
@RequestMapping(path = "v1/maintenance")
public class MaintenanceJSONController {

3. Connectez le contrôleur au repository de maintenance.

private final MaintenanceRepository maintenanceRepository;
   @Autowired
   public MaintenanceJSONController(MaintenanceRepository maintenanceRepository) {
       this.maintenanceRepository = maintenanceRepository;
   }

4. Ajoutez le gestionnaire d’endpoint.

@GetMapping(path="/", produces = "application/json")
public Iterable<MaintenanceIssue> MaintenanceForm( Model model) {
   return maintenanceRepository.findAll();
}

Une fois achevé, le nouvel ensemble de fonctionnalités ressemble à ceci :

package com.airbusiness.airbusinessmvc.controllers;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import com.airbusiness.airbusinessmvc.entities.MaintenanceIssue;

import com.airbusiness.airbusinessmvc.repositories.MaintenanceRepository;

@RestController
@RequestMapping(path = "v1/maintenance")
public class MaintenanceJSONController {
    private final MaintenanceRepository maintenanceRepository;
    
    @Autowired
    public MaintenanceJSONController(MaintenanceRepository maintenanceRepository) {
        this.maintenanceRepository = maintenanceRepository;
    }
    
    // localhost:8080/v1/maintenance/unfixed/
    @GetMapping(path="/unfixed", produces = "application/json")
    public Iterable<MaintenanceIssue> MaintenanceForm( Model model) {
        return maintenanceRepository.findByFixed("");
    }
}

L'avantage quand on fournit des fonctionnalités via une API : on ne risque pas de s'emmêler les pinceaux. Bon d’accord, c’est toujours possible de faire des erreurs : un numéro de téléphone peut  comporter une coquille par exemple.

Mais sinon, voici comment une API peut aider à prévenir des erreurs. Si une API n'inclut pas une certaine méthode, alors la fonctionnalité n’existe pas. Par exemple, s’il n’y a pas d’endpoint removeAllClients (supprimerTousLesClients), alors on ne peut pas accidentellement supprimer tous les clients. Comme je l’ai dit, c’est toujours possible. : vous pourriez tous les supprimer un par un. 😉

Au regard de ces éléments, ce qu'il faut garder en tête pour une API découplée, ce sont les descriptions JSON ou XML des données qui entrent et des données qui reviennent.

En résumé

  • Respectez la séparation des responsabilités lorsque vous implémentez vos différentes couches.

  • Les interactions entre les couches doivent toujours passer par une API bien définie.

  • Pour vous assurer que vos données sont bien définies dans votre API :

    • Déterminez l’intention et le format des données.

    • Pour vos messages entre les couches, définissez :

      • À quoi ressembleront vos données.

      • Les données obligatoires et les données facultatives.

      • Les ensembles de valeurs acceptables.

      • Les éventuelles modifications à venir.

Maintenant que nous avons regardé les messages entre les couches avec une API bien définie, regardons comment choisir le bon style de communication pour notre application !

Example of certificate of achievement
Example of certificate of achievement