ExpressJS Comment structurer une application?


528

J'utilise le framework Web ExpressJS pour NodeJS.

Les personnes utilisant ExpressJS mettent leurs environnements (développement, production, test ...), leurs itinéraires etc sur le app.js. Je pense que ce n'est pas une belle façon car lorsque vous avez une grosse application, app.js est trop gros!

Je voudrais avoir cette structure de répertoires:

| my-application
| -- app.js
| -- config/
     | -- environment.js
     | -- routes.js

Voici mon code:

app.js

var express = require('express');
var app = module.exports = express.createServer();

require('./config/environment.js')(app, express);
require('./config/routes.js')(app);

app.listen(3000);

config / environment.js

module.exports = function(app, express){
    app.configure(function() {
    app.use(express.logger());
    });

    app.configure('development', function() {
    app.use(express.errorHandler({
        dumpExceptions: true,
        showStack: true
    }));
    });

    app.configure('production', function() {
    app.use(express.errorHandler());
    });
};

config / routes.js

module.exports = function(app) {
    app.get('/', function(req, res) {
    res.send('Hello world !');
    });
};

Mon code fonctionne bien et je pense que la structure des répertoires est magnifique. Cependant, le code a dû être adapté et je ne suis pas sûr qu'il soit bon / beau.

Est-il préférable d'utiliser ma structure de répertoires et d'adapter le code ou d'utiliser simplement un fichier (app.js)?

Merci pour vos conseils!


Est-ce que les problèmes de performance de cette façon se cachent toujours? Je me souviens avoir lu quelque part (peut-être le groupe express) que lorsque vous séparez tout comme ça, vous perdez une tonne de performances. Quelque chose comme vos demandes / s diminuera considérablement, presque comme s'il s'agissait d'un bug.
AntelopeSalad

2
C'était du groupe Express Google. Voici le lien: groups.google.com/group/express-js/browse_thread/thread/…
AntelopeSalad

52
non c'est très faux
tjholowaychuk

Réponses:


306

OK, cela fait un moment et c'est une question populaire, alors j'ai continué et créé un référentiel github d'échafaudage avec du code JavaScript et un long README sur la façon dont j'aime structurer une application express.js de taille moyenne.

focusaurus / express_code_structure est le dépôt avec le dernier code pour cela. Les demandes de tirage sont les bienvenues.

Voici un instantané du fichier README puisque stackoverflow n'aime pas les réponses d'un simple lien. Je ferai quelques mises à jour car c'est un nouveau projet que je continuerai à mettre à jour, mais finalement le repo github sera le lieu à jour pour cette information.


Structure de code express

Ce projet est un exemple de la façon d'organiser une application Web express.js de taille moyenne.

Actuel au moins express v4.14 décembre 2016

Statut de la construction

style js standard

Quelle est la taille de votre application?

Les applications Web ne sont pas toutes les mêmes, et il n'y a pas, à mon avis, une structure de code unique qui devrait être appliquée à toutes les applications express.js.

Si votre application est petite, vous n'avez pas besoin d'une structure de répertoire aussi profonde que celle illustrée ici. Restez simple et collez une poignée de .jsfichiers à la racine de votre référentiel et vous avez terminé. Voilà.

Si votre application est énorme, vous devez à un moment donné la diviser en packages npm distincts. En général, l'approche node.js semble favoriser de nombreux petits packages, au moins pour les bibliothèques, et vous devez créer votre application en utilisant plusieurs packages npm car cela commence à avoir un sens et à justifier la surcharge. Donc, à mesure que votre application se développe et qu'une partie du code devient clairement réutilisable en dehors de votre application ou est un sous-système clair, déplacez-le vers son propre référentiel git et transformez-le en un package npm autonome.

L' objectif de ce projet est donc d'illustrer une structure viable pour une application de taille moyenne.

Quelle est votre architecture globale

Il existe de nombreuses approches pour créer une application Web, telles que

  • Côté serveur MVC à la Ruby on Rails
  • Style d'application de page unique à la MongoDB / Express / Angular / Node (MEAN)
  • Site Web de base avec quelques formulaires
  • Modèles / Opérations / Vues / Événements, le style à la MVC est mort, il est temps de bouger
  • et bien d'autres à la fois actuels et historiques

Chacun d'entre eux s'intègre parfaitement dans une structure de répertoires différente. Pour les besoins de cet exemple, il s'agit simplement d'un échafaudage et non d'une application pleinement fonctionnelle, mais j'assume les points d'architecture clés suivants:

  • Le site a des pages / modèles statiques traditionnels
  • La partie "application" du site est développée comme un style d'application à page unique
  • L'application expose une API de style REST / JSON au navigateur
  • L'application modélise un domaine commercial simple, dans ce cas, c'est une application de concessionnaire automobile

Et qu'en est-il de Ruby on Rails?

Ce sera un thème tout au long de ce projet que de nombreuses idées incarnées dans Ruby on Rails et les décisions "Convention sur la configuration" qu'ils ont adoptées, bien qu'elles soient largement acceptées et utilisées, ne sont en fait pas très utiles et sont parfois à l'opposé de ce que ce référentiel recommande.

Mon point principal ici est qu'il existe des principes sous-jacents à l'organisation du code, et sur la base de ces principes, les conventions Ruby on Rails ont un sens (principalement) pour la communauté Ruby on Rails. Cependant, le simple fait de réfléchir inconsciemment à ces conventions passe à côté. Une fois que vous aurez maîtrisé les principes de base, TOUS vos projets seront bien organisés et clairs: scripts shell, jeux, applications mobiles, projets d'entreprise, même votre répertoire personnel.

Pour la communauté Rails, ils veulent pouvoir faire passer un seul développeur Rails d'une application à une autre et être familier et à l'aise avec chaque fois. Cela a beaucoup de sens si vous êtes 37 signaux ou Pivotal Labs, et présente des avantages. Dans le monde JavaScript côté serveur, la philosophie globale est bien plus farfelue, et nous n'avons pas vraiment de problème avec cela. Voilà comment nous roulons. Nous y sommes habitués. Même dans express.js, c'est un proche parent de Sinatra, pas Rails, et prendre des conventions de Rails n'aide généralement rien. Je dirais même principes sur convention sur configuration .

Principes et motivations sous-jacents

  • Soyez gérable mentalement
    • Le cerveau ne peut traiter et penser qu'à un petit nombre de choses liées à la fois. C'est pourquoi nous utilisons des répertoires. Il nous aide à gérer la complexité en nous concentrant sur de petites portions.
  • Soyez approprié à la taille
    • Ne créez pas de «répertoires de manoirs» où il n'y a qu'un seul fichier, 3 répertoires en moins. Vous pouvez voir cela se produire dans les meilleures pratiques Ansible qui fait honte aux petits projets en créant plus de 10 répertoires pour contenir plus de 10 fichiers alors qu'un répertoire avec 3 fichiers serait beaucoup plus approprié. Vous ne conduisez pas un bus pour travailler (sauf si vous êtes un conducteur de bus, mais même si vous conduisez un bus AT ne fonctionne PAS), alors ne créez pas de structures de système de fichiers qui ne sont pas justifiées par les fichiers réels à l'intérieur .
  • Soyez modulaire mais pragmatique
    • La communauté de nœuds privilégie globalement les petits modules. Tout ce qui peut être complètement séparé de votre application doit être extrait dans un module pour un usage interne ou publié publiquement sur npm. Cependant, pour les applications de taille moyenne qui sont concernées ici, le surcoût peut ajouter de l'ennui à votre flux de travail sans valeur proportionnelle. Donc, pour le moment où vous avez du code qui est factorisé mais pas suffisant pour justifier un module npm complètement séparé, considérez-le simplement comme un " proto-module " avec l'espoir que quand il franchit un certain seuil de taille, il sera extrait.
    • Certaines personnes comme @ hij1nx incluent même un app/node_modulesrépertoire et ont des package.jsonfichiers dans les répertoires du proto-module pour faciliter cette transition et servir de rappel.
  • Soyez facile à localiser le code
    • Étant donné une fonctionnalité à créer ou un bogue à corriger, notre objectif est qu'un développeur n'ait aucune difficulté à localiser les fichiers source impliqués.
    • Les noms sont significatifs et précis
    • le code crufty est entièrement supprimé, pas laissé dans un fichier orphelin ou simplement commenté
  • Soyez facile à rechercher
    • tout le code source d'origine est dans le apprépertoire, vous pouvez donc cdy exécuter run / grep / xargs / ag / ack / etc et ne pas être distrait par des correspondances tierces
  • Utilisez un nom simple et évident
    • npm semble désormais exiger des noms de packages tout en minuscules. Je trouve cela surtout terrible mais je dois suivre le troupeau, donc les noms de fichiers doivent utiliser kebab-casemême si le nom de variable pour cela en JavaScript doit être camelCaseparce que -c'est un signe moins en JavaScript.
    • le nom de la variable correspond au nom de base du chemin du module, mais avec kebab-casetransformé encamelCase
  • Grouper par couplage, pas par fonction
    • Ceci est un changement majeur de la convention Ruby on Rails app/views, app/controllers, app/models, etc.
    • Les fonctionnalités sont ajoutées à une pile complète, je souhaite donc me concentrer sur une pile complète de fichiers pertinents pour ma fonctionnalité. Lorsque j'ajoute un champ de numéro de téléphone au modèle utilisateur, je ne me soucie d'aucun contrôleur autre que le contrôleur utilisateur et je ne me soucie d'aucun modèle autre que le modèle utilisateur.
    • Donc, au lieu de modifier 6 fichiers qui se trouvent chacun dans leur propre répertoire et d'ignorer des tonnes d'autres fichiers dans ces répertoires, ce référentiel est organisé de telle sorte que tous les fichiers dont j'ai besoin pour créer une fonctionnalité sont colocalisés
    • De par la nature de MVC, la vue utilisateur est couplée au contrôleur utilisateur qui est couplé au modèle utilisateur. Ainsi, lorsque je change de modèle d'utilisateur, ces 3 fichiers changent souvent ensemble, mais le contrôleur des transactions ou le contrôleur client sont découplés et donc pas impliqués. La même chose s'applique également aux conceptions non MVC.
    • Le découplage de style MVC ou MOVE en termes de code qui va dans quel module est toujours encouragé, mais la propagation des fichiers MVC dans les répertoires frères est juste ennuyeuse.
    • Ainsi, chacun de mes fichiers d'itinéraires a la partie des itinéraires qu'il possède. Un routes.rbfichier de style rails est pratique si vous voulez un aperçu de tous les itinéraires dans l'application, mais lorsque vous créez des fonctionnalités et corrigez des bugs, vous ne vous souciez que des itinéraires pertinents pour la pièce que vous modifiez.
  • Stocker les tests à côté du code
    • Ceci est juste un exemple de "groupe par couplage", mais je voulais l'appeler spécifiquement. J'ai écrit de nombreux projets où les tests vivent sous un système de fichiers parallèle appelé "tests" et maintenant que j'ai commencé à mettre mes tests dans le même répertoire que leur code correspondant, je ne reviendrai jamais. Ceci est plus modulaire et beaucoup plus facile à utiliser dans les éditeurs de texte et atténue une grande partie du non-sens du chemin "../../ ..". En cas de doute, essayez-le sur quelques projets et décidez par vous-même. Je ne vais rien faire au-delà de cela pour vous convaincre que c'est mieux.
  • Réduisez le couplage transversal avec les événements
    • Il est facile de penser «OK, chaque fois qu'une nouvelle offre est créée, je veux envoyer un e-mail à tous les vendeurs», puis il suffit de mettre le code pour envoyer ces e-mails dans l'itinéraire qui crée les offres.
    • Cependant, ce couplage finira par transformer votre application en une boule de boue géante.
    • Au lieu de cela, le DealModel devrait simplement déclencher un événement "create" et ignorer complètement ce que le système pourrait faire d'autre en réponse à cela.
    • Lorsque vous codez de cette façon, il devient beaucoup plus possible de mettre tout le code associé à l'utilisateur app/userscar il n'y a pas un nid de logique métier couplée partout polluant la pureté de la base de code utilisateur.
  • Le flux de code est suivable
    • Ne fais pas de choses magiques. Ne chargez pas automatiquement les fichiers à partir de répertoires magiques dans le système de fichiers. Ne soyez pas Rails. L'application démarre à app/server.js:1et vous pouvez voir tout ce qu'elle charge et exécute en suivant le code.
    • Ne faites pas de DSL pour vos itinéraires. Ne faites pas de métaprogrammation idiote quand ce n'est pas nécessaire.
    • Si votre application est si grand que cela magicRESTRouter.route(somecontroller, {except: 'POST'})est une grande victoire pour vous sur 3 de base app.get, app.put, app.del, les appels, vous êtes probablement construire une application monolithique qui est trop grand pour travailler efficacement. Obtenez fantaisie pour de GRANDES victoires, pas pour convertir 3 lignes simples en 1 ligne complexe.
  • Utiliser des noms de fichiers en minuscules

    • Ce format évite les problèmes de sensibilité à la casse du système de fichiers sur toutes les plateformes
    • npm interdit les majuscules dans les nouveaux noms de paquets, et cela fonctionne bien avec cela

      Spécificités express.js

  • Ne pas utiliser app.configure. C'est presque entièrement inutile et vous n'en avez tout simplement pas besoin. C'est dans beaucoup de passe-partout en raison de copypasta stupide.

  • L'ORDRE DU MIDWARE ET DES ITINÉRAIRES EN MATIÈRE EXPRESSE !!!
    • Presque tous les problèmes de routage que je vois sur stackoverflow sont des middlewares express hors service
    • En général, vous voulez que vos itinéraires soient découplés et ne dépendent pas beaucoup de l'ordre
    • Ne pas utiliser app.usepour l'ensemble de votre application si vous n'avez vraiment besoin que de ce middleware pour 2 routes (je vous regarde, body-parser)
    • Assurez-vous que tout est dit et fait, vous avez EXACTEMENT cette commande:
      1. Tout middleware très important à l'échelle de l'application
      2. Tous vos itinéraires et assortiments de middlewares
      3. ALORS gestionnaires d'erreur
  • Malheureusement, étant inspiré par sinatra, express.js suppose principalement que tous vos itinéraires seront entrés server.jset il sera clair comment ils sont commandés. Pour une application de taille moyenne, décomposer les choses en modules de routes séparés est bien, mais cela introduit un péril de middleware hors service

L'astuce du lien symbolique de l'application

Il existe de nombreuses approches décrites et discutées longuement par la communauté dans le grand fond mieux chemins exigent locale () pour Node.js . Je vais peut-être bientôt décider de préférer soit "traiter avec beaucoup de ../../../ .." ou utiliser le module requireFrom. Cependant, pour le moment, j'utilise l'astuce de lien symbolique détaillée ci-dessous.

Donc, une façon d'éviter l'intra-projet nécessite des chemins relatifs ennuyeux comme require("../../../config")d'utiliser l'astuce suivante:

  • créer un lien symbolique sous node_modules pour votre application
    • cd node_modules && ln -nsf ../app
  • ajoutez juste le lien symbolique node_modules / app lui - même , pas tout le dossier node_modules, pour git
    • git add -f node_modules / app
    • Oui, vous devriez toujours avoir "node_modules" dans votre .gitignorefichier
    • Non, vous ne devez pas mettre "node_modules" dans votre dépôt git. Certaines personnes vous recommanderont de le faire. Ils sont incorrects.
  • Maintenant, vous pouvez exiger des modules intra-projet en utilisant ce préfixe
    • var config = require("app/config");
    • var DealModel = require("app/deals/deal-model");
  • Fondamentalement, cela fait que l'intra-projet nécessite un travail très similaire à celui des modules npm externes.
  • Désolé, utilisateurs Windows, vous devez vous en tenir aux chemins relatifs du répertoire parent.

Configuration

Généralement, codez les modules et les classes pour qu'ils n'attendent qu'un optionsobjet JavaScript de base transmis. Seul app/server.jsdoit charger le app/config.jsmodule. À partir de là, il peut synthétiser de petits optionsobjets pour configurer les sous-systèmes selon les besoins, mais coupler chaque sous-système à un grand module de configuration global plein d'informations supplémentaires est un mauvais couplage.

Essayez de centraliser la création de connexions de base de données et de les transmettre aux sous-systèmes plutôt que de passer des paramètres de connexion et de demander aux sous-systèmes d'établir eux-mêmes les connexions sortantes.

NODE_ENV

Ceci est une autre idée séduisante mais terrible transmise par Rails. Il devrait y avoir exactement 1 place dans votre application, app/config.jsqui examine la NODE_ENVvariable d'environnement. Tout le reste doit prendre une option explicite comme argument de constructeur de classe ou paramètre de configuration de module.

Si le module de messagerie a une option sur la façon de livrer des e-mails (SMTP, connectez-vous à stdout, mettez en file d'attente, etc.), il devrait prendre une option comme {deliver: 'stdout'}mais il ne devrait absolument pas vérifier NODE_ENV.

Les tests

Je garde maintenant mes fichiers de test dans le même répertoire que leur code correspondant et j'utilise les conventions de dénomination des extensions de nom de fichier pour distinguer les tests du code de production.

  • foo.js a le code du module "foo"
  • foo.tape.js a les tests basés sur les nœuds pour foo et vit dans le même dir
  • foo.btape.js peut être utilisé pour les tests qui doivent s'exécuter dans un environnement de navigateur

J'utilise des globes de système de fichiers et la find . -name '*.tape.js'commande pour accéder à tous mes tests si nécessaire.

Comment organiser le code dans chaque .jsfichier de module

La portée de ce projet concerne principalement la destination des fichiers et des répertoires, et je ne veux pas ajouter beaucoup d'autre portée, mais je mentionnerai simplement que j'organise mon code en 3 sections distinctes.

  1. Le bloc d'ouverture de CommonJS nécessite des appels aux dépendances d'état
  2. Bloc de code principal de JavaScript pur. Pas de pollution CommonJS ici. Ne référencez pas les exportations, les modules ou les exigences.
  3. Bloc de fermeture de CommonJS pour configurer les exportations

1
Que dois-je utiliser à la place de bodyParser si je n'ai que peu de routes qui l'utilisent?
Ilan Frumer

3
J'ai trouvé ce que je cherchais ici: stackoverflow.com/questions/12418372/…
Ilan Frumer

1
@wlingke consultez gist.github.com/branneman/8048520 pour une discussion approfondie des approches disponibles à ce problème.
Peter Lyons

@peterLyons Merci d'avoir partagé cela. Après avoir lu, je pense que je vais écrire un script de démarrage. Merci!
wlingke

2
en ce qui concerne l' astuce Le lien symbolique de l'application , il y a ce petit module qui fait disparaître tous les problèmes
Hayko Koryun

157

MISE À JOUR (29/10/2013) : Veuillez également consulter mon autre réponse qui a JavaScript au lieu de CoffeeScript à la demande générale ainsi qu'un référentiel github standard et un README détaillé détaillant mes dernières recommandations sur ce sujet.

Config

Ce que tu fais est bien. J'aime avoir mon propre espace de noms de configuration dans un config.coffeefichier de niveau supérieur avec un espace de noms imbriqué comme celui-ci.

#Set the current environment to true in the env object
currentEnv = process.env.NODE_ENV or 'development'
exports.appName = "MyApp"
exports.env =
  production: false
  staging: false
  test: false
  development: false
exports.env[currentEnv] = true
exports.log =
  path: __dirname + "/var/log/app_#{currentEnv}.log"
exports.server =
  port: 9600
  #In staging and production, listen loopback. nginx listens on the network.
  ip: '127.0.0.1'
if currentEnv not in ['production', 'staging']
  exports.enableTests = true
  #Listen on all IPs in dev/test (for testing from other machines)
  exports.server.ip = '0.0.0.0'
exports.db =
  URL: "mongodb://localhost:27017/#{exports.appName.toLowerCase()}_#{currentEnv}"

C'est convivial pour l'édition sysadmin. Ensuite, quand j'ai besoin de quelque chose, comme les informations de connexion DB, c'est

require('./config').db.URL

Routes / Contrôleurs

J'aime laisser mes itinéraires à mes contrôleurs et les organiser dans un app/controllerssous-répertoire. Ensuite, je peux les charger et les laisser ajouter toutes les routes dont ils ont besoin.

Dans mon app/server.coffeefichier coffeescript, je fais:

[
  'api'
  'authorization'
  'authentication'
  'domains'
  'users'
  'stylesheets'
  'javascripts'
  'tests'
  'sales'
].map (controllerName) ->
  controller = require './controllers/' + controllerName
  controller.setup app

J'ai donc des fichiers comme:

app/controllers/api.coffee
app/controllers/authorization.coffee
app/controllers/authentication.coffee
app/controllers/domains.coffee

Et par exemple dans mon contrôleur de domaine, j'ai une setupfonction comme celle-ci.

exports.setup = (app) ->
  controller = new exports.DomainController
  route = '/domains'
  app.post route, controller.create
  app.put route, api.needId
  app.delete route, api.needId
  route = '/domains/:id'
  app.put route, controller.loadDomain, controller.update
  app.del route, controller.loadDomain, exports.delete
  app.get route, controller.loadDomain, (req, res) ->
    res.sendJSON req.domain, status.OK

Vues

Mettre des points de vue app/viewsdevient le lieu habituel. Je le présente comme ça.

app/views/layout.jade
app/views/about.jade
app/views/user/EditUser.jade
app/views/domain/EditDomain.jade

Fichiers statiques

Allez dans un publicsous - répertoire.

Github / Semver / NPM

Placez un fichier de démarque README.md à votre racine git repo pour github.

Placez un fichier package.json avec un numéro de version sémantique dans votre racine git repo pour NPM.


1
Salut Peter! J'aime vraiment cette approche que vous privilégiez. Je travaille sur la construction d'un projet express et je voudrais vraiment faire les choses dans le bon sens plutôt que de le pirater et de le mettre en place. Ce serait superbe si vous aviez un échantillon repo sur github et / ou un article de blog dessus.
suVasH .....

4
Ce dépôt a un tas de modèles que vous pouvez utiliser comme référence: github.com/focusaurus/peterlyons.com
Peter Lyons

75
Le script du café rend la lecture difficile: / Avez-vous une chance d'obtenir un montage JS vanille? Merci
toasted_flakes

1
Merci pour cette réponse. J'essaie juste d'envelopper mon esprit. Comment accédez-vous aux autres contrôleurs à l'intérieur d'un autre (par exemple dans la fonction de configuration comme ciapp.put route, api.needId
chmanie

@PeterLyons: hey man, j'ai vu votre code source mais je n'ai aucune idée de comment faire le mode de construction, j'ai déjà installé Goet inclus le binfichier dans la structure. Comment exécutez-vous ce gofichier bin?
user2002495

51

Ce qui suit est la réponse textuellement de Peter Lyons, porté sur vanilla JS de Coffeescript, comme demandé par plusieurs autres. La réponse de Peter est très compétente, et toute personne qui vote sur ma réponse devrait également voter sur la sienne.


Config

Ce que tu fais est bien. J'aime avoir mon propre espace de noms de configuration dans un config.jsfichier de niveau supérieur avec un espace de noms imbriqué comme celui-ci.

// Set the current environment to true in the env object
var currentEnv = process.env.NODE_ENV || 'development';
exports.appName = "MyApp";
exports.env = {
  production: false,
  staging: false,
  test: false,
  development: false
};  
exports.env[currentEnv] = true;
exports.log = {
  path: __dirname + "/var/log/app_#{currentEnv}.log"
};  
exports.server = {
  port: 9600,
  // In staging and production, listen loopback. nginx listens on the network.
  ip: '127.0.0.1'
};  
if (currentEnv != 'production' && currentEnv != 'staging') {
  exports.enableTests = true;
  // Listen on all IPs in dev/test (for testing from other machines)
  exports.server.ip = '0.0.0.0';
};
exports.db {
  URL: "mongodb://localhost:27017/#{exports.appName.toLowerCase()}_#{currentEnv}"
};

C'est convivial pour l'édition sysadmin. Ensuite, quand j'ai besoin de quelque chose, comme les informations de connexion DB, c'est

require('./config').db.URL

Routes / Contrôleurs

J'aime laisser mes itinéraires à mes contrôleurs et les organiser dans un app/controllerssous-répertoire. Ensuite, je peux les charger et les laisser ajouter toutes les routes dont ils ont besoin.

Dans mon app/server.jsfichier javascript je fais:

[
  'api',
  'authorization',
  'authentication',
  'domains',
  'users',
  'stylesheets',
  'javascripts',
  'tests',
  'sales'
].map(function(controllerName){
  var controller = require('./controllers/' + controllerName);
  controller.setup(app);
});

J'ai donc des fichiers comme:

app/controllers/api.js
app/controllers/authorization.js
app/controllers/authentication.js
app/controllers/domains.js

Et par exemple dans mon contrôleur de domaine, j'ai une setupfonction comme celle-ci.

exports.setup = function(app) {
  var controller = new exports.DomainController();
  var route = '/domains';
  app.post(route, controller.create);
  app.put(route, api.needId);
  app.delete(route, api.needId);
  route = '/domains/:id';
  app.put(route, controller.loadDomain, controller.update);
  app.del(route, controller.loadDomain, function(req, res){
    res.sendJSON(req.domain, status.OK);
  });
}

Vues

Mettre des points de vue app/viewsdevient le lieu habituel. Je le présente comme ça.

app/views/layout.jade
app/views/about.jade
app/views/user/EditUser.jade
app/views/domain/EditDomain.jade

Fichiers statiques

Allez dans un publicsous - répertoire.

Github / Semver / NPM

Placez un fichier de démarque README.md à votre racine git repo pour github.

Placez un fichier package.json avec un numéro de version sémantique dans votre racine git repo pour NPM.


43

Ma question a été introduite en avril 2011, elle est calme depuis longtemps. Pendant ce temps, j'ai pu améliorer mon expérience avec Express.js et comment l'architecture d'une application écrite à l'aide de cette bibliothèque. Je partage donc ici mon expérience.

Voici ma structure de répertoire:

├── app.js   // main entry
├── config   // The configuration of my applications (logger, global config, ...)
├── models   // The model data (e.g. Mongoose model)
├── public   // The public directory (client-side code)
├── routes   // The route definitions and implementations
├── services // The standalone services (Database service, Email service, ...)
└── views    // The view rendered by the server to the client (e.g. Jade, EJS, ...)

App.js

Le but du app.jsfichier est d'amorcer l'application expressjs. Il charge le module de configuration, le module d'enregistreur, attend la connexion à la base de données, ... et exécute le serveur express.

'use strict';
require('./config');
var database = require('./services/database');
var express = require('express');
var app = express();
module.exports = app;

function main() {
  var http = require('http');

  // Configure the application.
  app.configure(function () {
    // ... ... ...
  });
  app.configure('production', function () {
    // ... ... ...
  });
  app.configure('development', function () {
    // ... ... ...
  });

  var server = http.createServer(app);

  // Load all routes.
  require('./routes')(app);

  // Listen on http port.
  server.listen(3000);
}

database.connect(function (err) {
  if (err) { 
    // ...
  }
  main();
});

itinéraires /

Le répertoire des routes a un index.jsfichier. Son objectif est d'introduire une sorte de magie pour charger tous les autres fichiers dans le routes/répertoire. Voici l'implémentation:

/**
 * This module loads dynamically all routes modules located in the routes/
 * directory.
 */
'use strict';
var fs = require('fs');
var path = require('path');

module.exports = function (app) {
  fs.readdirSync('./routes').forEach(function (file) {
    // Avoid to read this current file.
    if (file === path.basename(__filename)) { return; }

    // Load the route file.
    require('./' + file)(app);
  });
};

Avec ce module, créer une nouvelle définition et implémentation de route est vraiment facile. Pour des exemples, hello.js:

function hello(req, res) {
  res.send('Hello world');
}

module.exports = function (app) {
  app.get('/api/hello_world', hello);
};

Chaque module de route est autonome .


Utilisez-vous un générateur pour créer cette structure?
Ashish


17

Je pense que c'est une excellente façon de le faire. Non limité à exprimer, mais j'ai vu un certain nombre de projets node.js sur github faire la même chose. Ils suppriment les paramètres de configuration + les modules plus petits (dans certains cas, chaque URI) sont pris en compte dans des fichiers séparés.

Je recommanderais de passer par des projets spécifiques à express sur github pour avoir une idée. IMO la façon dont vous faites est correcte.


16

c'est maintenant fin 2015 et après avoir développé ma structure pendant 3 ans et en petits et grands projets. Conclusion?

Ne faites pas un grand MVC, mais séparez-le en modules

Donc...

Pourquoi?

  • Habituellement, on travaille sur un module (par exemple, les produits), que vous pouvez modifier indépendamment.

  • Vous êtes capable de réutiliser des modules

  • Vous pouvez le tester séparément

  • Vous pouvez le remplacer séparément

  • Ils ont des interfaces claires (stables)

    -Au plus tard, si plusieurs développeurs travaillaient, la séparation des modules aide

Le projet nodebootstrap a une approche similaire à ma structure finale. ( github )

À quoi ressemble cette structure?

  1. Petits modules capsulés , chacun avec MVC séparé

  2. Chaque module a un package.json

  3. Test dans le cadre de la structure (dans chaque module)

  4. Configuration globale , bibliothèques et services

  5. Docker intégré, cluster, pour toujours

Présentation du dossier (voir le dossier lib pour les modules):

structure nodebootstrap


3
Il serait utile que vous puissiez également mettre à jour l'image de présentation du dossier avec les modules individuels développés, comme exemple de la façon dont vous les structureriez également.
youngrrrr

8

Je donne la structure de dossier de style MVC, veuillez trouver ci-dessous.

Nous avons utilisé la structure de dossiers ci-dessous pour nos applications Web grandes et moyennes.

 myapp   
|
|
|____app
|      |____controllers
|      |    |____home.js
|      |
|      |____models
|      |     |___home.js
|      |
|      |____views
|           |___404.ejs
|           |___error.ejs
|           |___index.ejs
|           |___login.ejs
|           |___signup.ejs
|   
|
|_____config
|     |___auth.js
|     |___constants.js
|     |___database.js
|     |___passport.js
|     |___routes.js
|
|
|____lib
|    |___email.js
|
|____node_modules
|
|
|____public.js
|    |____css
|    |    |__style.css
|    |    
|    |____js
|    |    |__script.js
|    |
|    |____img
|    |    |__img.jpg
|    |
|    |
|    |____uploads
|         |__img.jpg
|      
|   
|
|_____app.js
|
|
|
|_____package.json

J'ai créé un module npm pour la structuration de dossiers mvc express de génération.

Veuillez trouver le https://www.npmjs.com/package/express-mvc-generator ci-dessous

Des étapes simples pour générer et utiliser ces modules.

i) installer le module npm install express-mvc-generator -g

ii) vérifier les options express -h

iii) Générer une structure mvc express express myapp

iv) Installer les dépendances npm install::

v) Ouvrez votre config / database.js, veuillez configurer votre mongo db.

vi) Exécutez l'application node appounodemon app

vii) Vérifiez l'URL http: // localhost: 8042 / signup OU http: // yourip: 8042 / signup


7

Cela fait un bon moment depuis la dernière réponse à cette question et Express a également récemment publié la version 4, qui a ajouté quelques éléments utiles pour organiser la structure de votre application.

Vous trouverez ci-dessous un long article de blog à jour sur les meilleures pratiques pour structurer votre application Express. http://www.terlici.com/2014/08/25/best-practices-express-structure.html

Il existe également un référentiel GitHub appliquant les conseils de l'article. Il est toujours à jour avec la dernière version Express.
https://github.com/terlici/base-express


7

Je ne pense pas que ce soit une bonne approche pour ajouter des routes à la configuration. Une meilleure structure pourrait être quelque chose comme ceci:

application/
| - app.js
| - config.js
| - public/ (assets - js, css, images)
| - views/ (all your views files)
| - libraries/ (you can also call it modules/ or routes/)
    | - users.js
    | - products.js
    | - etc...

Ainsi, products.js et users.js contiendront toutes vos routes et toute la logique à l'intérieur.


6

Eh bien, je mets mes itinéraires sous forme de fichier json, que j'ai lu au début, et dans une boucle for dans app.js, j'ai configuré les itinéraires. Le fichier route.json inclut la vue à appeler et la clé des valeurs qui seront envoyées à la route.
Cela fonctionne pour de nombreux cas simples, mais j'ai dû créer manuellement des itinéraires pour des cas spéciaux.


6

J'ai écrit un article exactement à ce sujet. Il utilise essentiellement un routeRegistrarqui itère dans les fichiers du dossier /controllersappelant sa fonction init. La fonction initprend la appvariable express comme paramètre afin que vous puissiez enregistrer vos itinéraires comme vous le souhaitez.

var fs = require("fs");
var express = require("express");
var app = express();

var controllersFolderPath = __dirname + "/controllers/";
fs.readdirSync(controllersFolderPath).forEach(function(controllerName){
    if(controllerName.indexOf("Controller.js") !== -1){
        var controller = require(controllersFolderPath + controllerName);
        controller.init(app);
    }
});

app.listen(3000);


4

1) Votre système de fichiers de projet Express peut ressembler à:

/ ...
/lib
/node_modules
/public
/views
      app.js
      config.json
      package.json

app.js - votre conteneur d'applications global

2) Fichier principal du module (lib / mymodule / index.js):

var express = require('express');    
var app = module.exports = express();
// and load module dependencies ...  

// this place to set module settings
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');

// then do module staff    
app.get('/mymodule/route/',function(req,res){ res.send('module works!') });

3) Connectez le module dans app.js principal

...
var mymodule = require('mymodule');
app.use(mymodule);

4) Exemple de logique

lib/login
lib/db
lib/config
lib/users
lib/verify
lib/
   /api/ 
   ...
lib/
   /admin/
      /users/
      /settings/
      /groups/
...
  • Idéal pour les tests
  • Idéal pour l'échelle
  • Séparer dépend du module
  • Regroupement d'itinéraire par fonctionnalité (ou modules)

tj dit / montre sur Vimeo une idée intéressante comment modulariser une application express - Applications web modulaires avec Node.js et Express . Puissant et simple.



3

J'ai récemment adopté des modules en tant que mini-applications indépendantes.

|-- src
  |--module1
  |--module2
     |--www
       |--img
       |--js
       |--css
     |--#.js
     |--index.ejs
  |--module3
  |--www
     |--bower_components
     |--img
     |--js
     |--css
  |--#.js
  |--header.ejs
  |--index.ejs
  |--footer.ejs

Maintenant, pour tout routage de module (# .js), les vues (* .ejs), js, css et assets sont côte à côte. le routage des sous-modules est configuré dans le parent # .js avec deux lignes supplémentaires

router.use('/module2', opt_middleware_check, require('./module2/#'));
router.use(express.static(path.join(__dirname, 'www')));

De cette façon, même des sous-modules sont possibles.

N'oubliez pas de définir la vue dans le répertoire src

app.set('views', path.join(__dirname, 'src'));

tout lien vers github avec une telle structure intéressé à voir comment les itinéraires, les vues et les modèles sont chargés
Muhammad Umer

Je pense que tout est expliqué. Les routes ne sont que des routes express classiques. Les vues doivent être chargées avec le préfixe des noms de module, les modèles doivent être chargés en référençant le chemin relatif.
zevero

Sur ma dernière ligne, j'ai défini la vue dans le répertoire src. Donc à partir de maintenant, toutes les vues sont accessibles par rapport au répertoire src. Rien d'extraordinaire.
zevero

1

Voici à quoi ressemble la plupart de ma structure de répertoire de projet express.

Je fais habituellement un express dirnamepour initialiser le projet, pardonnez ma paresse, mais c'est très flexible et extensible. PS - vous devez vous en procurer express-generator(pour ceux qui le recherchent sudo npm install -g express-generator, sudo parce que vous l'installez mondialement)

|-- bin
    |-- www //what we start with "forever"
|-- bower_components
|-- models
    |-- database.js
    |-- model1.js //not this exact name ofcourse.
    |-- .
|-- node_modules
|-- public
    |-- images
    |-- javascripts
        |-- controllers
        |-- directives
        |-- services
        |-- app.js
        |-- init.js //contains config and used for initializing everything, I work with angular a lot.
    |-- stylesheets
|-- routes
    |-- some
    |-- hierarchy
    .
    .
|-- views
    |-- partials
    |-- content
|-- .env
|-- .env.template
|-- app.js
|-- README.md

Vous devez vous demander pourquoi les fichiers .env? Parce qu'ils fonctionnent! J'utilise le dotenvmodule dans mes projets (beaucoup récemment) et ça marche! Insérez ces 2 déclarations dans app.jsouwww

var dotenv = require('dotenv');
dotenv.config({path: path.join(__dirname + "/.env")});

Et une autre ligne à définir rapidement /bower_componentspour diffuser du contenu statique sous la ressource/ext

app.use('/ext', express.static(path.join(__dirname, 'bower_components')));

Cela peut probablement convenir aux personnes qui cherchent à utiliser Express et Angular ensemble, ou tout simplement à exprimer sans cette javascriptshiérarchie bien sûr.


1

Ma structure express 4. https://github.com/odirleiborgert/borgert-express-boilerplate

Paquets

View engine: twig
Security: helmet
Flash: express-flash
Session: express-session
Encrypt: bcryptjs
Modules: express-load
Database: MongoDB
    ORM: Mongoose
    Mongoose Paginate
    Mongoose Validator
Logs: winston + winston-daily-rotate-file
Nodemon
CSS: stylus
Eslint + Husky

Structure

|-- app
    |-- controllers
    |-- helpers
    |-- middlewares
    |-- models
    |-- routes
    |-- services
|-- bin
|-- logs
|-- node_modules
|-- public
    |-- components
    |-- images
    |-- javascripts
    |-- stylesheets
|-- views
|-- .env
|-- .env-example
|-- app.js
|-- README.md

0

Un moyen simple de structurer votre application express:

  • Dans index.js principal, l'ordre suivant doit être conservé.

    tout app.set doit être le premier.

    tout app.use devrait être le deuxième.

    suivi d'autres apis avec leurs fonctions ou route-continue dans d'autres fichiers

    Exapmle

    app.use ("/ mot de passe", passwordApi);

    app.use ("/ user", userApi);

    app.post ("/ token", passport.createToken);

    app.post ("/ logout", passport.logout)


0

Meilleur moyen de structure MVC pour le projet ExpressJs avec guidon et Passportjs

- app
      -config 
        -passport-setup.js
      -controllers
      -middleware
      -models
      -routes
      -service
    -bin
      -www
      -configuration.js
      -passport.js
    -node_modules
    -views
     -handlebars page
    -env
    -.gitignore
    -package.json
    -package-lock.json

@ sandro-munda s'il vous plaît vérifier
Manishkumar Bhavnani
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.