Appuyez sur Entrée pour voir vos résultats ou Echap pour annuler.

Une couche supplémentaire dans le frontend ?

Dans la plupart des applications que j’ai vues bâties avec un framework comme Angular, React ou Vue, il n’y avait pas vraiment de couche supplémentaire dans l’architecture frontend autre que les composantes qui sont fournies dans ces frameworks. Puis, j’en suis venu à développer un prototype d’application avec Angular et je cherchais une façon rapide de tester. Et si on traitait nos services comme une base de données ?

Note : Cet article est purement expérimental. J’aimerais bien avoir votre opinion en commentaire 🙂

La problématique

Supposons que mon objectif soit de faire rapidement un prototype simple d’une application Angular. Pour pouvoir pleinement développer les fonctionnalités et faire une démo, j’aimerais qu’il y ait de la persistance au niveau des données que j’y saisie. J’ai vu quelques solutions originales, comme json-server et d’autres frameworks qui mockent les APIs directement. Il y a un avantage à cette méthode lorsqu’on construit du code de production parce que ça permet de définir les entrées / sorties que le frontend va avoir besoin en facilitant l’intégration avec les vraies APIs par la suite. Par contre, il y a un coût d’intégration, aussi minime soit-il.

Puis, je me suis rappelé un mandat au Ministry of Forests, Lands and Natural Resource de Colombie Britannique. L’application Angular devait pouvoir tomber en mode hors-ligne, puisqu’elle était utilisée dans des mines sous terre. C’était fait avec le localStorage

Proposition

Dans une application traditionnelle à plusieurs couches, on a MV quelque chose pour le frontend, puis on a une couche de service, une couche d’accès aux données, puis le système de persistance des données (BD, fichiers, etc.). On peut retrouver parfois cette structure dans l’architecture des applications en services REST.

En considérant que les frontends d’aujourd’hui sont des applications complètes en soit comme c’est le cas dans cette article en utilisant le localStorage comme système de persistance, on pourrait alors imagine un backend au strict minimum, soit appliquer l’authentification et la validation des accès pour tous les appels qui nécessitent un accès aux données.

Mais Sylvain… ça ressemble à des microservices ça 😛 !

Ouaip! Mais on s’intéresse au frontend! J’ai vu quelques articles intéressants (ang) qui proposent une architecture clean (ang) au niveau d’Angular. Mais j’aimerais explorer une autre voie, un peu plus simple, en considérant le design pattern qu’est le repository.

Avantages

Le premier, c’est la rapidité d’implémentation. L’objectif étant d’avoir un type générique qui fera pour tous les différents types d’objet du domaine. Un deuxième est au niveau de l’évolutivité. En ayant une couche dédiée à l’accès aux données frontend, ça permet notamment de passer d’une implémentation à l’autre via l’injection de dépendances. De plus, en ayant une implémentation via le localStorage, par exemple, ça pourrait aussi faciliter l’écriture de tests unitaires frontend, considérant qu’il peut être réinitialisé assez facilement.

Inconvénients

Évidemment, il y en a. En ayant une implémentation générique, ça nous limite un peu dans la personnalisation des différents appels au backend. Tous les services doivent être identiques, soit :

  • trouver tous les objets d’un type spécifique
  • trouver un objet par son identifiant unique
  • trouver tous les objets d’un type spécifique qui correspond à certains critères
  • ajouter un objet
  • mettre à jour un objet
  • retirer un objet

Implémentation

localStorage

Dans un premier temps, on doit créer une interface qui va définir le contrat que toutes les implémentations du repository vont devoir respecter. Voici un exemple :

Ici, le collectionName va nous permettre de stocker les objets du même type dans une même entrée du localStorage. On pourrait aussi, dans le cas d’un accès à des API via HTTP, mettre ce nom de collection dans l’URL pour appeler le bon service (ex. : /api/clients/findAll). Puis, on crée une implémentation pour écrire les données dans le localStorage :

Ensuite on implémente les méthodes pour que ça compile. Commençons par la plus simple :

Le principe étant d’obtenir les données depuis le localStorage au format sérialisé en chaîne de caractères, puis de tenter de les retourner. Sauf qu’on a un problème avec les propriétés des objets stockés… Si un type contient une ou plusieurs propriétés de type Date, elles ne seront pas désérialisées en objet Date, mais bien en chaîne de caractères. On pourrait remédier à la situation comme ceci :

Je vous laisser faire les autres méthodes 🙂 Vous devriez comprendre le principe.

http

Maintenant, on crée une deuxième implémentation pour notre backend. Celle-ci sera évidemment beaucoup plus simple, mais à titre comparatif, voici l’implémentation partielle :

FindCriteria

L’implémentation ci-dessus est volontairement simple. Le challenge ici, c’est de créer un système de filtre commun. Heureusement, le type any de JavaScript nous permet de faciliter le filtre en passant par exemple :

De cette façon, on a beaucoup de liberté, mais il faudra assurément appliquer une certaine validation à l’attribut filter s’il est envoyé au backend pour éviter une faille de sécurité.

Injection du service

Au niveau des providers du module Angular, il faut simplement lui en définir un comme ceci :

Ou encore, en se basant sur une variable d’environnement comme ceci :

Puis, pour l’injection, il faut utiliser @Inject() et lui fournir le nom du provider créé plus haut :

Conclusion

Comme je l’expliquais en début d’article, tout ceci n’était qu’une expérimentation que j’ai fini par trouver assez agréable. Je suis curieux d’avoir votre opinion en commentaire 🙂 Faites-vous quelque chose qui ressemble à ça aussi dans vos équipes ou vous avez l’habitude de vous en tenir à ce qui est offert par le framework?

Suivez-nous par courriel!

Saisissez votre adresse courriel pour vous abonner au blog d'Ezo et recevoir une notification de chaque nouvel article par email.