Architecturer de grosses applications Node.js avec Architect

Choisir un framework et des guidelines pour architecturer et rendre scalable votre application Node.js n'est pas une chose aisée. La communauté de développeur Node.js offre une multitude de frameworks bien souvent incomplets ou trop peu éprouvés sur de gros projets.

Aujourd'hui, je souhaiterais vous présenter la proposition d'architecture faîtes par la société Cloud9. Certains d'entre vous connaissent sans doute cette société qui délivre un éditeur de code en ligne, principalement développé en Node.js.

Le framework développé par Cloud9, nommé Architect, a pour avantage d'être en production depuis plusieurs années, dans un projet à fort besoin de performance. Architect est donc un framework fiable, éprouvé mais également simple et élégant.

Les objectifs du framework

Architect est une structure pour vos applications Node, simple mais puissante. A l'aide d'une configuration, vous déclarez une liste de plugins Architect que vous souhaitez charger. Ces plugins que vous développez peuvent avoir des dépendances vers d'autres plugins Architect. Mieux encore, ces plugins peuvent être importés dans votre gestionnaire de paquet NPM privé et réutilisés dans vos autres projets.

Ainsi, Architect répond de la façon la plus minimale possible à trois problèmes essentiels :

  • Séparer les responsabilités des différents composants du projet
  • Réutiliser vos composants d'un projet à l'autre
  • Vous laissez libre d'utiliser toutes vos librairies préférées (Express, Mongoose, Hapi...)

La structure d'Architect étant très légère, vous pouvez rapidement prototyper un projet utilisant Architect, Express, Mongoose et autres.

J'ai d'ailleurs partagé en 2013 un Boilerplate minimal pour Node utilisant Architect, Mongoose, Express et Passport. Loin d'être fini, ce projet permet d'avoir en moins de 3 minutes un aperçu d'Architect.

La structure d'un projet Architect

Un projet Architect se décompose en plugins. Chaque plugins pouvant "consommer" d'autres plugins, créant un ensemble de dépendances.

Les plugins

Un plugin n'est rien d'autre qu'une fonction exportée vers Architect. Cette fonction permet d'initialiser le plugin. Pour ce faire, elle prend trois paramètres : options, imports et register.

Paramètre options
Chaque plugin peut avoir une configuration spécifique. Cette configuration est passée lors de l'initialisation du plugin dans le paramètre options.

Paramètre imports
Comme dit précédemment, un plugin peut en consommer un autre, c'est à dire l'utiliser. Les plugins que vous avez choisi d'importer seront disponibles dans le paramètre imports.

Paramètre register
Architect considère que l'initialisation du plugin peut être asynchrone. On peut par exemple imaginer une connexion à la base de donnée. C'est pourquoi Architect fourni un callback que vous devez appeler pour indiquer que le plugin est chargé intégralement sans erreur. C'est le rôle de la fonction register(null).

De plus, dans le cas ou vous souhaiteriez que votre plugin expose certaines de ces fonctions à d'autres plugins, vous avez la possibilité de l'indiquer dans register.

Voyez ci-dessous l'exemple d'un wrapper pour ExpressJs sous forme de Plugin Architect.

var express = require('express');  
var port    = options.port;  
var app     = express();

module.exports = function initializePlugin(options, imports, register) {  
    app.use(express.cookieParser());

    register(null, {
        app: {
            get:        function (route, handler) {
                            app.get(route, handler);
                        },
            put:        function (route, handler) {
                            app.put(route, handler);
                        },
            post:       function (route, handler) {
                            app.post(route, handler);
                        },
            del:        function (route, handler) {
                            app.delete(route, handler);
                        },
            middleware: function (routes) {
                            app.use(routes);
                        }
        }

    });

    if(app.listen(port)) {
        console.info("Server launched");
    }
};

.

Indépendance des plugins

Chaque plugin Architect est isolé. Il répond à un usage précis et dispose d'une liste explicite de dépendances. Ainsi, il peut facilement être exporté dans un gestionnaire de paquet tel que NPM.

Pour faciliter cette pratique, Architect force l'utilisation d'un fichier package.json pour chaque plugin. Ce fichier défini les propriétés du paquet NPM mais également la liste des dépendances du plugin vis à vis des autres plugins de l'application.

{
    "name": "auth",
    "version": "0.0.1",
    "main": "auth.js",
    "private": true,

    "plugin": {
        "consumes": ["mongoose", "app"],
        "provides": ["auth"]
    }
}

Chaque plugin peut consommer des fonctionnalités externes ou partager les siennes. C'est la signification des tableaux consumes et provides.

Les limites d'Architect

Architect est un projet relativement peu populaire bien que très éprouvé par Cloud9. Pour autant, sa simplicité est garante de la bonne utilisation du produit. La communauté, bien que petite, est suffisante au support du projet.

En revanche un point noir réside dans l'absence, "out of the box", d'une méthode pour exécuter des tests unitaires et fonctionnels sur vos plugins.
Naturellement, c'est possible avec quelques recherches. Vous pouvez trouver des pistes sur l'issue #31 d'Architect.

A noter également que les dépendances cycliques posent problème avec Architect.

En savoir plus

Pour en savoir plus sur Node.js et devenir autonome sur vos projets d'entreprises, suivez notre formation sur mesure.