Redux est un excellent outil pour gérer l'état des applications frontales complexes. L'auteur du document, dont nous publions la traduction aujourd'hui, va trouver la réponse à la question de savoir s'il est possible de profiter des fonctionnalités de Redux dans l'environnement serveur.
Pourquoi ai-je besoin d'une bibliothèque Redux?
La
page d'accueil de la bibliothèque Redux indique qu'il s'agit d'un «conteneur d'état prévisible pour les applications JavaScript». Redux est généralement appelé un outil pour gérer l'état d'une application, et bien que cette bibliothèque soit principalement utilisée avec React, elle peut être utilisée dans tous les projets basés sur JavaScript.
Nous avons déjà mentionné que Redux est utilisé pour contrôler l'état d'une application. Parlons maintenant de ce qu'est une «condition». Ce concept est assez difficile à définir, mais nous essayons toujours de le décrire.
En considérant l '"état", si nous parlons de personnes ou d'objets du monde matériel, nous cherchons à décrire, en fait, leur état au moment où nous en parlons, en considérant éventuellement un ou plusieurs paramètres. Par exemple, nous pouvons dire au sujet du lac: "L'eau est très chaude" ou: "L'eau est gelée". Dans ces déclarations, nous décrivons l'état du lac en termes de température de l'eau.
Quand quelqu'un dit de lui-même: «Je suis échoué», il considère le montant d'argent dont il dispose. Il est clair que dans chacun de ces exemples, nous ne parlons que d'un aspect de l'état des objets. Mais, dans l'exemple de l'argent, la déclaration peut être celle qui décrit plusieurs paramètres: "Je suis échoué, je n'ai pas mangé depuis longtemps, mais je suis heureux!". Il est très important de noter ici que l'État est quelque chose d'impermanent. Cela signifie que cela peut changer. Par conséquent, lorsque nous apprenons l'état actuel d'un certain objet, nous comprenons que son état réel peut changer quelques secondes ou minutes après que nous en ayons pris connaissance.
Lorsque nous traitons de programmes, certaines fonctionnalités sont associées au concept d '«état». Premièrement, l'état de l'application est représenté par des données stockées quelque part. Par exemple, ces données peuvent être stockées en mémoire (par exemple, en tant qu'objet JavaScript), mais elles peuvent être stockées dans un fichier, dans une base de données et en utilisant un mécanisme de mise en cache comme Redis. Deuxièmement, l'état d'une application est généralement lié à son instance spécifique. Par conséquent, lorsque nous parlons de l'état de l'application, nous entendons une instance spécifique de cette application, un processus, un environnement de travail organisé dans l'application pour un utilisateur spécifique. Un état d'application peut inclure, par exemple, les informations suivantes:
- L'utilisateur est-il connecté ou non? Si oui, combien de temps dure la session et quand va-t-elle expirer?
- Combien de points l'utilisateur a-t-il marqués? Une telle question est pertinente, par exemple, pour un certain jeu.
- Où l'utilisateur a-t-il mis la vidéo en pause? Cette question peut être posée sur l'application du lecteur vidéo.
Si nous parlons de l'état des applications à un niveau inférieur, cela peut inclure, par exemple, les informations suivantes:
- Quelles variables sont définies dans l'environnement actuel dans lequel l'application s'exécute (il s'agit des «variables d'environnement»).
- Quels fichiers le programme utilise-t-il actuellement?
En regardant le «snapshot» (ils sont souvent appelés «snapshots» - à partir d'un instantané) du statut de l'application à tout moment, nous pouvons en apprendre davantage sur les conditions dans lesquelles l'application a fonctionné à ce moment et, si nécessaire, recréer ces conditions en application à l'état dans lequel il se trouvait au moment de la réception de l'instantané.
L'état peut être modifié lors de l'exécution de certaines actions par l'utilisateur. Par exemple, si l'utilisateur déplace correctement le personnage du jeu dans un jeu simple, cela peut augmenter le nombre de points. Dans des applications assez complexes, l'approche de modification d'un état peut devenir plus compliquée; les changements d'état peuvent provenir de différentes sources.
Par exemple, dans une partie multijoueur, le nombre de points qu'un utilisateur marque dépend non seulement de ses actions, mais aussi des actions de ceux qui jouent avec lui dans la même équipe. Et si un personnage contrôlé par ordinateur attaque avec succès un personnage de jeu que l'utilisateur contrôle, l'utilisateur peut perdre un certain nombre de points.
Imaginez que nous développons une application frontale comme
PWA Twitter . Il s'agit d'une application d'une page qui comporte plusieurs onglets, par exemple - Accueil, Recherche, Notifications et Messages. Chacun de ces onglets possède son propre espace de travail, qui est destiné à la fois à l'affichage de certaines informations et à sa modification. Toutes ces données constituent l'état de l'application. Ainsi, de nouveaux tweets, notifications et messages arrivent dans l'application toutes les quelques secondes. L'utilisateur peut travailler avec le programme et avec ces données. Par exemple, il peut créer un tweet ou le supprimer, il peut retweeter un tweet, il peut lire des notifications, envoyer des messages à quelqu'un, etc. Tout ce qui vient d'être discuté modifie l'état de l'application.
Tous ces onglets ont leur propre ensemble de composants d'interface utilisateur utilisés pour afficher et modifier les données. L'état de l'application peut être affecté par les données entrant dans l'application de l'extérieur et les actions de l'utilisateurIl est clair que dans une telle application, les sources de changements d'état peuvent être différentes entités, tandis que les changements initiés par différentes sources peuvent se produire presque simultanément. Si nous gérons l'état manuellement, il peut s'avérer difficile pour nous de surveiller ce qui se passe. Ces difficultés conduisent à des contradictions. Par exemple, un tweet peut être supprimé, mais il sera toujours affiché dans le flux de tweet. Ou, disons, l'utilisateur peut lire la notification ou le message, mais il sera toujours affiché dans le programme comme non visualisé.
L'utilisateur peut aimer le tweet, un cœur apparaîtra dans l'interface du programme, mais une requête réseau qui envoie des informations similaires au serveur ne fonctionnera pas. Par conséquent, ce que l'utilisateur voit sera différent de ce qui est stocké sur le serveur. C'est pour éviter de telles situations que Redux peut être nécessaire.
Comment fonctionne Redux?
Dans la bibliothèque Redux, il existe trois concepts principaux qui visent à rendre la gestion de l'état des applications simple et directe:
- Stockage (magasin). Un référentiel Redux est un objet JavaScript qui représente l'état d'une application. Il joue le rôle de "la seule source de données fiables". Cela signifie que l'application entière doit s'appuyer sur le stockage comme seule entité responsable de la représentation de l'État.
- Actions Le magasin d'état est en lecture seule. Cela signifie qu'il ne peut pas être modifié en y accédant directement. La seule façon de modifier le contenu du référentiel est d'utiliser des actions. Tout composant qui souhaite changer d'état doit prendre les mesures appropriées.
- Réducteurs (réducteurs), également appelés "convertisseurs". Un réducteur est une fonction pure qui décrit comment un état est modifié par des actions. Le réducteur prend l'état et l'action actuels, dont l'exécution a été demandée par un certain composant de l'application, après quoi il retourne l'état transformé.
L'utilisation de ces trois concepts signifie que l'application ne doit plus surveiller directement les événements qui sont des sources de changements d'état (actions de l'utilisateur, réponses API, l'occurrence d'événements associés à la réception de certaines données via le protocole WebSocket, etc.) et prendre des décisions sur la façon dont ces événements affecteront la condition.
En utilisant le modèle Redux, ces événements peuvent déclencher des actions qui changeront d'état. Les composants qui ont besoin d'utiliser des données stockées dans l'état de l'application peuvent simplement s'abonner aux changements d'état et recevoir des informations qui les intéressent. En utilisant tous ces mécanismes, Redux s'engage à apporter des changements prévisibles à l'état de l'application.
Voici un exemple schématique qui montre comment organiser un système de gestion d'état simple à l'aide de Redux dans notre application fictive:
import { createStore } from 'redux';
En prenant ce code comme base, nous pouvons équiper notre système de gestion de l'état de l'application avec des actions supplémentaires et les envoyer depuis différents endroits de l'application sans risquer de confusion désespérée.
Voici le matériel à partir duquel vous pouvez en apprendre davantage sur les trois principes fondamentaux de Redux.
Parlons maintenant de l'utilisation de Redux dans un environnement de serveur.
Migration des principes Redux vers l'environnement du serveur
Nous avons exploré les fonctionnalités de Redux utilisées dans le développement d'applications clientes. Mais, étant donné que Redux est une bibliothèque JavaScript, elle peut théoriquement également être utilisée dans un environnement de serveur. Nous réfléchirons à la façon dont les principes ci-dessus peuvent être appliqués sur le serveur.
Vous vous souvenez de la façon dont nous avons parlé de l'état de l'application client? Il convient de noter qu'il existe des différences conceptuelles entre les applications client et serveur. Ainsi, les applications clientes ont tendance à maintenir l'état entre divers événements, par exemple, entre l'exécution des demandes au serveur. Ces applications sont appelées applications avec état.
S'ils ne s'efforçaient pas de stocker l'état, alors, par exemple, lorsqu'il travaille avec un certain service Web qui nécessite un identifiant et un mot de passe, l'utilisateur devrait effectuer cette procédure chaque fois qu'il accède à une nouvelle page de l'interface Web correspondante.
Les applications dorsales, en revanche, s'efforcent de ne pas stocker l'état (elles sont également appelées applications sans état). Ici, en parlant d '«applications backend», nous entendons principalement des projets basés sur certaines API distinctes des applications frontales. Cela signifie que des informations sur l'état du système doivent être fournies à des applications similaires à chaque accès. Par exemple, l'API ne contrôle pas si l'utilisateur est connecté ou non. Il détermine son état en analysant le jeton d'authentification dans ses demandes à cette API.
Cela nous amène à une raison importante pour laquelle Redux ne serait guère utilisé sur des serveurs sous la forme dans laquelle nous avons décrit ses capacités ci-dessus.
Le fait est que Redux a été conçu pour stocker l'état temporaire de l'application. Mais l'état de l'application stockée sur le serveur doit généralement exister suffisamment longtemps. Si vous utilisiez le référentiel Redux dans votre application Node.js côté serveur, l'état de cette application serait effacé à chaque arrêt du processus de
node
. Et si nous parlons d'un serveur PHP qui implémente un schéma de gestion d'état similaire, alors l'état sera effacé lorsque chaque nouvelle demande arrivera sur le serveur.
La situation est encore plus compliquée si l'on considère les applications serveur en termes d'évolutivité. Si vous deviez faire évoluer l'application horizontalement, en augmentant le nombre de serveurs, vous auriez alors de nombreux processus Node.js en cours d'exécution en même temps, et chacun aurait sa propre option d'état. Cela signifie qu'avec la réception simultanée de deux demandes identiques au backend, des réponses différentes auraient pu être données.
Comment appliquer les principes de gestion des états dont nous avons discuté sur le serveur? Jetons un autre regard sur les concepts Redux et voyons comment ils sont généralement utilisés dans un environnement de serveur:
- Dépôt. Sur le backend, «la seule source de données fiables» est généralement une base de données. Parfois, afin de faciliter l'accès aux données qui sont souvent nécessaires, ou pour une autre raison, une copie d'une partie de cette base de données peut être faite - sous la forme d'un cache ou sous la forme d'un fichier. En règle générale, ces copies sont en lecture seule. Les mécanismes qui les contrôlent sont abonnés aux modifications dans le référentiel principal et, lorsque ces modifications se produisent, mettent à jour le contenu des copies.
- Actions et réducteurs. Ce sont les seuls mécanismes utilisés pour changer d'état. Dans la plupart des applications backend, le code est écrit dans un style impératif, ce qui n'est pas particulièrement propice à l'utilisation de concepts d'action et de réducteurs.
Considérons deux modèles de conception qui, par leur nature, sont similaires à ce que la bibliothèque Redux vise à faire. Ce sont CQRS et Event Sourcing. En fait, ils ont comparu devant Redux, leur mise en œuvre peut être extrêmement difficile, nous allons donc en parler très brièvement.
CQRS et sourçage d'événements
CQRS (Command Query Responsibility Segregation) est un modèle de conception, dans la mise en œuvre duquel l'application lit les données du magasin uniquement à l'aide de requêtes et écrit uniquement à l'aide de commandes.
Lorsque vous utilisez CQRS, la seule façon de changer l'état d'une application est d'envoyer une commande. Les commandes sont similaires aux actions Redux. Par exemple, dans Redux, vous pouvez écrire du code qui correspond à ce schéma:
const action = { type: 'CREATE_NEW_USER', payload: ... }; store.dispatch(action);
Lorsque vous utilisez CQRS, quelque chose comme ceci ressemblerait à ceci:
Les requêtes sont des mécanismes de lecture de données dans le modèle CQRS. Ils sont équivalents à la construction
store.getState()
. Dans une implémentation CQRS simple, les requêtes interagiront directement avec la base de données, récupérant les enregistrements de celle-ci.
Le modèle Event Sourcing est conçu pour enregistrer toutes les modifications de l'état de l'application sous la forme d'une séquence d'événements. Ce modèle est mieux adapté aux applications qui doivent connaître non seulement leur état actuel, mais également l'historique de ses modifications, la façon dont l'application a atteint son état actuel. À titre d'exemples, vous pouvez citer l'historique des opérations avec les comptes bancaires, le suivi des colis, le traitement des commandes dans les magasins en ligne, l'organisation du transport de marchandises, la logistique.
Voici un exemple d'implémentation du modèle Event Sourcing:
Ce qui se passe ici ressemble également à l'utilisation des actions Redux.
Cependant, le mécanisme d'enregistrement des événements organise également le stockage à long terme des informations sur chaque changement d'état, et pas seulement le stockage de l'état lui-même. Cela nous permet de reproduire ces modifications au point dans le temps dont nous avons besoin, rétablissant ainsi le contenu de l'état de l'application à ce moment. Par exemple, si nous devons comprendre combien d'argent était dans le compte bancaire pour une certaine date, nous devons seulement reproduire les événements qui se sont produits avec le compte bancaire jusqu'à ce que nous arrivions à la bonne date. Les événements dans ce cas sont représentés par des entrées de fonds sur le compte et par leur débit, par le débit de la commission bancaire et d'autres opérations similaires. Si des erreurs se produisent (c'est-à-dire lorsque des événements contenant des données incorrectes se produisent), nous pouvons invalider l'état actuel de l'application, corriger les données correspondantes et revenir à l'état actuel de l'application, désormais formé sans erreur.
Les modèles CQRS et Event Sourcing sont souvent utilisés ensemble. Et, fait intéressant, Redux, en fait, est en partie basé sur ces modèles. Les commandes peuvent être écrites de manière à ce qu'elles soient envoyées, lorsqu'elles sont appelées. Ensuite, les événements interagissent avec le référentiel (base de données) et mettent à jour l'état. Dans les applications en temps réel, les objets de requête peuvent également écouter les événements et recevoir des informations d'état mises à jour du référentiel.
L'utilisation de l'un de ces modèles dans une application simple peut compliquer inutilement la tâche. Cependant, dans le cas d'applications conçues pour résoudre des problèmes commerciaux complexes, CQRS et Event Sourcing sont de puissantes abstractions qui aident à mieux modéliser le domaine de ces applications et à améliorer leur gestion d'état.
Veuillez noter que les modèles CQRS et Event Sourcing peuvent être implémentés de différentes manières, tandis que certaines de leurs implémentations sont plus complexes que d'autres. Nous n'avons considéré que des exemples très simples de leur mise en œuvre. Si vous écrivez des applications serveur dans Node.js, jetez un œil à
wolkenkit . Ce cadre, parmi ce qui a été trouvé dans ce domaine, fournit au développeur l'une des interfaces les plus simples pour implémenter les modèles CQRS et Event Sourcing.
Résumé
Redux est un excellent outil pour gérer l'état d'une application, afin de rendre les changements d'état prévisibles. Dans cet article, nous avons parlé des concepts clés de cette bibliothèque et constaté que, bien que l'utilisation de Redux dans un environnement de serveur ne soit probablement pas une bonne idée, vous pouvez appliquer des principes similaires sur un serveur à l'aide des modèles
CQRS et
Event Sourcing .
Chers lecteurs! Comment organisez-vous la gestion de l'état des applications client et serveur?
