NodeJS, la meilleure solution fullstack ?

Aurore de Amaral
Meetic
Published in
6 min readOct 12, 2022
Photo by AltumCode on Unsplash

Comment applique-t-on les principes de l’architecture hexagonale avec le langage JavaScript ?

Comment peut-on apporter une solution qui tire profit du parallélisme alors que ce langage est mono thread ?

Enfin, et c’est là tout le sujet de cet article, NodeJS ne serait-il pas LA solution qui permet de s’abstraire du duo back/front et de mutualiser les connaissances et expérience de chacun pour faire un produit qui tue ?

Je vais répondre à ma façon à toutes ces questions au travers d’une expérience concrète et très enrichissante à Meetic. Je vais tenter de le nuancer à la fin par rapport à mes propres attentes.

Récapitulatif des concepts

Je fais un récapitulatif des concepts techniques abordés ici :

  • NodeJS **est un runtime qui permet d’exécuter le langage JavaScript sur un serveur (et donc pas sur le navigateur). Au final, pas de NodeJS, pas d’article (ou pas de chocolat 🍫), c’est la base technique.
  • Les outils “dev experience” JavaScript et NodeJS sont très nombreux, mais j’en cite deux : npm qui est un gestionnaire de paquets et gère donc toutes les dépendances de votre projet. Enfin, nvm est un gestionnaire de version de NodeJS, ce qui est pratique si vous avez des versions de NodeJS différentes entre vos projets en local sur votre machine.
  • Programmation avec types statiquesse dit d’un langage qui permet de définir des types qui sont vérifiés dans une phase de compilation qui peut échouer s'ils ne sont pas valides avec l’attendu.
  • TypeScript est un moteur de “transcompilation” (= compilation) qui traduit un code JavaScript avec des types statiques (et pleins d’autres choses) en JavaScript.
  • Le principe de l’ architecture hexagonale est de découpler dans son code source ce qui relève des interfaces depuis et vers l’extérieur de notre code métier à travers divers patterns, souvent repris du BDD (Behavior Driven Design).
  • Un système est dit multi thread quand il peut être exécuté de manière simultanée (ou quasiment) sur plusieurs threads ou machines. Dans le cas contraire, il s’agit d’un système mono thread
  • Une application serverless est exécutée dans le cloud et implémentée indépendamment de l’infrastructure qu’il y a derrière. Ni le développeur, ni l’exploitant n’a la possibilité de modifier cette infrastructure. Le coût appliqué dépend de l’utilisation de cette infrastructure.

Un projet fullstack

Il arrive souvent que les projets web sont construits avec une barrière très forte : la partie front (ou l’UI) est codée en JavaScript et elle va communiquer avec des serveurs en utilisant une API pour avoir des données. Enfin, la partie back (le centre de données) est codée en whatever le langage et fournit une API pour abstraire les données et gérer l’authentification, faire des calculs complexes …

Il y a sûrement des façons de faire autrement, mais il y avait un parti pris dans l’équipe où j’ai travaillé :

Le “front” et le “back” étaient codés dans le même langage de programmation pour permettre la pluralité des expériences et connaissances.

Il y avait donc React et TypeScript pour le front, NodeJS et TypeScript pour le back. Et en effet, cela permet une aisance pour passer de l’un à l’autre pour les développeurs. C’était devenu une équipe pluridisciplinaire qui avait au moins connaissance de tout le code source et pouvait le lire.

C’est alors que je me suis demandée : Est-ce que pour délivrer de la valeur au produit, cela n’était pas la meilleure idée du monde que de briser un mur supplémentaire à ces silos techniques que l’on connait si bien (le front, le back, l’opérationnel et le mobile au fond…)

Où mettre de la valeur ?

Oui mais… comment peut-on créer une équipe s’il n’y a pas la maîtrise de tous les sujets ? Et soyons honnêtes, personne n’est bon partout (même le meilleur avouera bien qu’il ne sait pas faire quelque chose — aka le mythe du 10x developer)

Chacun de nous doit être aussi à l’aise avec les outils qu’il utilise (j’utilise toujours le mot outil dans son sens premier : une extension de la main pour créer quelque chose) et peut-être que cet outil n’est pas JavaScript ou NodeJS, mais dans ce cas il suffirait de se former auprès de maîtres de la discipline ?

Enfin, cette valeur n’a de sens que si elle est instaurée par l’équipe qui a commencé le produit et valorisé dans l’entreprise où elle a été mise en place. Elle pourra être mieux comprise par les nouveaux arrivants, qu’ils soient maîtres ou élèves de leur propres connaissances. En soit, il ne s’agit pas d’une baguette magique.

Elle peut permettre d’ajouter grandement de maîtrise technique à tous les participants du projet.

Il pourrait aussi faciliter une lecture potentielle du code source à des non-développeurs, s’il s’agit du même langage.

Exemple de code BDD en TypeScript

Pour illustrer le propos d’une architecture orientée behavior, proposons un cas, tel qu’il était testé dans notre équipe. Lisible pour tous. Pour tester l’application qui permet de donner le statut d’une personne, voici le test :

import { dependencies } from '../dependencies';
import { isOnline } from '../domain/is-online';
// Correspond à un port qui sera injecté statiquement
const { port } = dependencies;
// simuler l'appel externe
describe('my application', () => {
const givenName = (name: string) => ({
hasResponse: (promise: boolean) => {
port.response = jest.fn(async (value) => {
expect(value).toStrictEqual(name);
return promise;
});
},
});
// tester avec la méthode given-when-then
it('must say if a user is online', async () => {
// Given
givenName('toto').hasResponse(true);
// When
const result = await isOnline('toto');
// Then
expect(result).toEqual(true);
});
})

En effet, ici le code métier n’a pas beaucoup plus de valeur ajoutée, il s’agit uniquement d’un passe plat vers la réponse de l’API, mais c’est uniquement pour illustrer le cas écrit en TypeScript.

Voici le code dans la couche métier :

import { dependencies } from "../dependencies";// interface uniquement utilisée ici
export interface OnlineFetcher {
response(name: string): Promise<boolean>;
}
// code basique (passe plat)
export const isOnline = async (name: string): Promise<boolean> => {
return await dependencies.port.response(name);
};

Ce code a besoin d’un port secondaire (ou SPI) qui va se charger de faire l’appel HTTP (interface OnlineFetcher). Pour rappel, voici le modèle classique d’une architecture hexagonale :

Celui-ci est implémenté simplement dans l’exemple ci-dessous, n’importe quel client HTTP fait l’affaire mais ici pas d’exemple implémenté par souci d’exhaustivité. L’avantage d’utiliser une interface dans la couche métier est la facilité à laquelle on pourra changer de client HTTP au besoin.

import { OnlineFetcher } from "../domain/is-online";export const restOnlineFetcher = (apiKey: string): OnlineFetcher => ({
response: async (name: string): Promise<boolean> => {
console.log(apiKey, name);
//api call
return Promise.reject("no implemented API call");
},
});

Cette question se pose aussi dans le cadre d’un composant React, mais d’autres personnes en parleront sûrement mieux que moi (via des hooks et des tests appropriés).

Vous pourrez retrouver le code source de l’exemple sur GitHub.

Mais JavaScript est mono thread !

Là c’est fâcheux, notamment si l’on aime jouer avec la concurrence quand on est développeur backend. Mais NodeJS a une solution qui permet de pallier à cette limitation avec sa boucle d’événements et les thread worker.

Mais on pourrait voir plus loin : Pourquoi pas des fonctions en TypeScript qui se lancent en parallèle, supportant une charge élevée, et qui scale à la demande ? Sans avoir à s’occuper du serveur qui le fait tourner ? Cette promesse des technologies serverless est l’autre solution qui a été utilisée par mon équipe pour répondre à ces besoins de concurrence. Le choix s’était porté sur le cloud AWS.

Conclusion

JavaScript associé à TypeScript est un super langage, j’en suis convaincue, bien que je viens de l’univers Java. Cette expérience m’a permis de me questionner sur cette séparation entre développeurs backend et frontend.

Mais comme dit avant, NodeJS ne sera pas la réponse à tout. Donc pas de baguette magique.

Plus que le langage, il s’agit d’un changement de posture à avoir. Est-ce que les développeurs devraient forcément travailler dans leur coin pour être efficaces ? Est-ce que la communication inter-équipe doit seulement passer par la définition d’une API ou d’un GraphQL ?

--

--

Aurore de Amaral
Meetic
Writer for

French developer for several years interested in new medias such as conversational experiences. Code in Scala and NodeJS