ExpressJS ¿Cómo estructurar una aplicación?

528

Estoy usando el framework web ExpressJS para NodeJS.

Las personas que usan ExpressJS ponen sus entornos (desarrollo, producción, pruebas ...), sus rutas, etc en el app.js. Creo que no es una manera hermosa porque cuando tienes una aplicación grande, ¡app.js es demasiado grande!

Me gustaría tener esta estructura de directorios:

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

Aquí está mi código:

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 !');
    });
};

Mi código funciona bien y creo que la estructura de los directorios es hermosa. Sin embargo, el código tuvo que adaptarse y no estoy seguro de que sea bueno / hermoso.

¿Es mejor usar mi estructura de directorios y adaptar el código o simplemente usar un archivo (app.js)?

¡Gracias por tus consejos!

Sandro Munda
fuente
¿Siguen los problemas de rendimiento al hacerlo de esta manera? Recuerdo haber leído en alguna parte (tal vez el grupo express) que cuando separas todo de esta manera pierdes un montón de rendimiento. Algo así como sus requisitos / seg caerá en una cantidad notable, casi como si fuera un error.
AntelopeSalad
2
Era del grupo Express Google. Aquí está el enlace: groups.google.com/group/express-js/browse_thread/thread/…
AntelopeSalad
52
no, esto es muy falso
tjholowaychuk

Respuestas:

306

OK, ha pasado un tiempo y esta es una pregunta popular, así que seguí adelante y creé un repositorio de andamios github con código JavaScript y un largo archivo README sobre cómo me gusta estructurar una aplicación express.js de tamaño mediano.

focusaurus / express_code_structure es el repositorio con el último código para esto. Solicitudes de extracción bienvenidas.

Aquí hay una instantánea de README ya que stackoverflow no le gusta las respuestas de solo un enlace. Haré algunas actualizaciones ya que este es un nuevo proyecto que continuaré actualizando, pero finalmente el repositorio de github será el lugar actualizado para esta información.


Estructura de código expreso

Este proyecto es un ejemplo de cómo organizar una aplicación web express.js de tamaño mediano.

Actual al menos expreso v4.14 de diciembre de 2016

Estado de compilación

js-standard-style

¿Qué tan grande es su aplicación?

Las aplicaciones web no son todas iguales y, en mi opinión, no hay una única estructura de código que deba aplicarse a todas las aplicaciones express.js.

Si su aplicación es pequeña, no necesita una estructura de directorio tan profunda como se ejemplifica aquí. Simplemente manténgalo simple y pegue un puñado de .jsarchivos en la raíz de su repositorio y listo. Voilà.

Si su aplicación es enorme, en algún momento deberá dividirla en paquetes npm distintos. En general, el enfoque de node.js parece favorecer muchos paquetes pequeños, al menos para las bibliotecas, y debe construir su aplicación utilizando varios paquetes npm, ya que eso comienza a tener sentido y justifica la sobrecarga. Entonces, a medida que su aplicación crece y una parte del código se vuelve claramente reutilizable fuera de su aplicación o es un subsistema claro, muévalo a su propio repositorio git y conviértalo en un paquete npm independiente.

Por lo tanto, el objetivo de este proyecto es ilustrar una estructura viable para una aplicación de tamaño mediano.

¿Cuál es su arquitectura general?

Existen muchos enfoques para crear una aplicación web, como

  • Lado del servidor MVC a la Ruby on Rails
  • Estilo de aplicación de página única a la MongoDB / Express / Angular / Node (MEAN)
  • Sitio web básico con algunas formas
  • Estilo de Modelos / Operaciones / Vistas / Eventos a la MVC está muerto, es hora de MUDARSE
  • y muchos otros tanto actuales como históricos

Cada uno de estos encaja perfectamente en una estructura de directorio diferente. Para los propósitos de este ejemplo, solo se trata de andamios y no es una aplicación que funciona completamente, pero supongo que los siguientes puntos clave de la arquitectura:

  • El sitio tiene algunas páginas / plantillas estáticas tradicionales.
  • La parte de "aplicación" del sitio se desarrolla como un estilo de aplicación de página única
  • La aplicación expone una API de estilo REST / JSON al navegador
  • La aplicación modela un dominio comercial simple, en este caso, es una aplicación de concesionario de automóviles

¿Y qué hay de Ruby on Rails?

Será un tema a lo largo de este proyecto que muchas de las ideas incorporadas en Ruby on Rails y las decisiones de "Convención sobre la configuración" que han adoptado, aunque ampliamente aceptadas y utilizadas, en realidad no son muy útiles y a veces son lo contrario de lo que este repositorio recomienda

Mi punto principal aquí es que existen principios subyacentes para organizar el código, y basados ​​en esos principios, las convenciones de Ruby on Rails tienen sentido (principalmente) para la comunidad de Ruby on Rails. Sin embargo, simplemente imitar esas convenciones sin pensarlo pasa por alto el punto. Una vez que comprenda los principios básicos, TODOS sus proyectos estarán bien organizados y claros: scripts de shell, juegos, aplicaciones móviles, proyectos empresariales, incluso su directorio de inicio.

Para la comunidad de Rails, quieren poder tener un solo desarrollador de Rails para cambiar de una aplicación a otra y estar familiarizados y cómodos con él cada vez. Esto tiene mucho sentido si tiene 37 señales o Pivotal Labs, y tiene beneficios. En el mundo de JavaScript del lado del servidor, el ethos general es mucho más salvaje que cualquier cosa, y realmente no tenemos ningún problema con eso. Así es como nosotros lo hacemos. Estamos acostumbrados Incluso dentro de express.js, es un pariente cercano de Sinatra, no de Rails, y tomar convenciones de Rails generalmente no ayuda en nada. Incluso diría Principios sobre Convención sobre Configuración .

Principios y motivaciones subyacentes

  • Ser mentalmente manejable
    • El cerebro solo puede lidiar y pensar en un pequeño número de cosas relacionadas a la vez. Por eso usamos directorios. Nos ayuda a lidiar con la complejidad al enfocarnos en porciones pequeñas.
  • Ser apropiado para el tamaño
    • No cree "Directorios de Mansion" donde solo hay 1 archivo, solo 3 directorios abajo. Puede ver que esto suceda en las Mejores prácticas de Ansible que avergüenza a los proyectos pequeños para crear más de 10 directorios para contener más de 10 archivos cuando 1 directorio con 3 archivos sería mucho más apropiado. No conduce un autobús al trabajo (a menos que sea un conductor de autobús, pero incluso así conduce un autobús EN el trabajo no PARA trabajar), así que no cree estructuras de sistema de archivos que no estén justificadas por los archivos reales dentro de ellos .
  • Sé modular pero pragmático
    • La comunidad de nodos en general favorece los módulos pequeños. Cualquier cosa que pueda separarse de su aplicación por completo debe extraerse en un módulo para uso interno o publicarse públicamente en npm. Sin embargo, para las aplicaciones medianas que son el alcance aquí, la sobrecarga de esto puede agregar tedio a su flujo de trabajo sin un valor proporcional. Entonces, para el momento en que tenga algún código que se factoriza pero no lo suficiente como para justificar un módulo npm completamente separado, solo considérelo un " proto-módulo " con la expectativa de que cuando cruce algún umbral de tamaño, se extraiga.
    • Algunas personas como @ hij1nx incluso incluyen un app/node_modulesdirectorio y tienen package.jsonarchivos en los directorios del proto-módulo para facilitar esa transición y actuar como un recordatorio.
  • Sea fácil de localizar el código
    • Dada una característica para construir o un error para corregir, nuestro objetivo es que un desarrollador no tenga problemas para localizar los archivos fuente involucrados.
    • Los nombres son significativos y precisos.
    • el código crufty se elimina por completo, no se deja en un archivo huérfano o simplemente se comenta
  • Sea amigable con la búsqueda
    • todo el código fuente de origen está en el appdirectorio para que pueda cdejecutar find / grep / xargs / ag / ack / etc y no distraerse con coincidencias de terceros
  • Use nombres simples y obvios
    • npm ahora parece requerir nombres de paquete en minúsculas. Esto me parece terriblemente terrible, pero debo seguir al rebaño, por lo tanto, los nombres de archivo deben usarse kebab-caseaunque el nombre de la variable para eso en JavaScript debe ser camelCaseporque -es un signo menos en JavaScript.
    • el nombre de la variable coincide con el nombre base de la ruta del módulo, pero con kebab-casetransformado acamelCase
  • Agrupar por acoplamiento, no por función
    • Este es un cambio importante de la convención de Ruby on Rails app/views, app/controllers, app/models, etc.
    • Las características se agregan a una pila completa, por lo que quiero centrarme en una pila completa de archivos que sean relevantes para mi característica. Cuando agrego un campo de número de teléfono al modelo de usuario, no me importa ningún controlador que no sea el controlador de usuario, y no me importa ningún modelo que no sea el modelo de usuario.
    • Entonces, en lugar de editar 6 archivos que están cada uno en su propio directorio e ignorar toneladas de otros archivos en esos directorios, este repositorio está organizado de tal manera que todos los archivos que necesito para construir una función se colocan
    • Por la naturaleza de MVC, la vista de usuario está acoplada al controlador de usuario que está acoplado al modelo de usuario. Entonces, cuando cambio el modelo de usuario, esos 3 archivos a menudo cambian juntos, pero el controlador de ofertas o el controlador del cliente están desacoplados y, por lo tanto, no están involucrados. Lo mismo se aplica a los diseños que no son MVC por lo general también.
    • El desacoplamiento de estilo MVC o MOVE en términos de qué código va en qué módulo todavía se recomienda, pero distribuir los archivos MVC en directorios hermanos es simplemente molesto.
    • Por lo tanto, cada uno de mis archivos de rutas tiene la parte de las rutas que posee. Un routes.rbarchivo de estilo de rieles es útil si desea una visión general de todas las rutas en la aplicación, pero cuando realmente crea características y corrige errores, solo le importan las rutas relevantes para la pieza que está cambiando.
  • Almacenar pruebas al lado del código
    • Esto es solo una instancia de "grupo por acoplamiento", pero quería llamarlo específicamente. He escrito muchos proyectos donde las pruebas viven bajo un sistema de archivos paralelo llamado "pruebas" y ahora que he comenzado a colocar mis pruebas en el mismo directorio que su código correspondiente, nunca volveré. Esto es más modular y mucho más fácil de trabajar en editores de texto y alivia muchas de las tonterías de la ruta "../../ ..". Si tienes dudas, pruébalo en algunos proyectos y decide por ti mismo. No voy a hacer nada más que esto para convencerte de que es mejor.
  • Reduzca el acoplamiento transversal con eventos
    • Es fácil pensar "OK, cada vez que se crea un nuevo acuerdo, quiero enviar un correo electrónico a todos los vendedores", y luego simplemente poner el código para enviar esos correos electrónicos en la ruta que crea los acuerdos.
    • Sin embargo, este acoplamiento eventualmente convertirá su aplicación en una bola gigante de lodo.
    • En cambio, el DealModel debería disparar un evento de "creación" y desconocer por completo qué más podría hacer el sistema en respuesta a eso.
    • Cuando codifica de esta manera, se hace mucho más posible poner todo el código relacionado con el usuario app/usersporque no hay un nido de ratas de lógica comercial acoplada en todo el lugar contaminando la pureza de la base de código de usuario.
  • El flujo de código es seguible
    • No hagas cosas mágicas. No cargue automáticamente archivos de directorios mágicos en el sistema de archivos. No seas rieles. La aplicación se inicia en app/server.js:1y puede ver todo lo que carga y ejecuta siguiendo el código.
    • No hagas DSL para tus rutas. No hagas una metaprogramación tonta cuando no sea necesario.
    • Si su aplicación es tan grande que hacer magicRESTRouter.route(somecontroller, {except: 'POST'})es una gran victoria para usted durante 3 básica app.get, app.put, app.del, llamadas, es probable que la construcción de una aplicación monolítica que es demasiado grande para trabajar eficazmente en. Obtenga fantasía para GRANDES victorias, no para convertir 3 líneas simples en 1 línea compleja.
  • Utilice nombres de archivo en minúsculas kebab

    • Este formato evita problemas de mayúsculas y minúsculas en el sistema de archivos en todas las plataformas
    • npm prohíbe mayúsculas en nuevos nombres de paquetes, y esto funciona bien con eso

      Detalles de express.js

  • No utilice app.configure. Es casi completamente inútil y simplemente no lo necesitas. Está en un montón de repeticiones debido a la copia sin sentido.

  • ¡LA ORDEN DE MIDDLEWARE Y RUTAS EN MATERIA EXPRESA!
    • Casi todos los problemas de enrutamiento que veo en stackoverflow son middleware express fuera de servicio
    • En general, desea que sus rutas estén desacopladas y no dependan tanto del orden
    • No lo use app.usepara toda su aplicación si realmente solo necesita ese middleware para 2 rutas (lo estoy mirando body-parser)
    • Asegúrese de que cuando todo esté dicho y hecho tenga EXACTAMENTE este pedido:
      1. Cualquier middleware súper importante para toda la aplicación
      2. Todas sus rutas y surtido de rutas de middlewares
      3. ENTONCES manejadores de errores
  • Lamentablemente, al estar inspirado en sinatra, express.js asume principalmente que todas sus rutas estarán en server.jsy estará claro cómo se ordenan. Para una aplicación de tamaño mediano, dividir las cosas en módulos de rutas separadas es bueno, pero presenta un peligro de middleware fuera de servicio

El truco del enlace simbólico de la aplicación

Hay muchos enfoques descritos y discutidos ampliamente por la comunidad en la gran GIST Mejor locales requieren caminos () para Node.js . Pronto puedo decidir preferir "tratar con muchos ../../../ .." o usar el módulo requireFrom. Sin embargo, por el momento, he estado usando el truco de enlace simbólico que se detalla a continuación.

Entonces, una forma de evitar el intraproyecto requiere con rutas relativas molestas como require("../../../config")es usar el siguiente truco:

  • crear un enlace simbólico debajo de node_modules para su aplicación
    • cd node_modules && ln -nsf ../app
  • agregue solo el enlace simbólico node_modules / app en sí , no toda la carpeta node_modules, a git
    • git add -f node_modules / app
    • Sí, aún debe tener "node_modules" en su .gitignorearchivo
    • No, no debe poner "node_modules" en su repositorio git. Algunas personas te recomendarán que hagas esto. Son incorrectos
  • Ahora puede requerir módulos dentro del proyecto utilizando este prefijo
    • var config = require("app/config");
    • var DealModel = require("app/deals/deal-model");
  • Básicamente, esto hace que el trabajo dentro del proyecto requiera un trabajo muy similar al requerido para los módulos npm externos.
  • Lo sentimos, usuarios de Windows, deben seguir las rutas relativas del directorio principal.

Configuración

Generalmente, los módulos y clases de código esperan que solo se optionspase un objeto JavaScript básico . Solo se app/server.jsdebe cargar el app/config.jsmódulo. A partir de ahí, puede sintetizar pequeños optionsobjetos para configurar subsistemas según sea necesario, pero acoplar cada subsistema a un gran módulo de configuración global lleno de información adicional es un mal acoplamiento.

Intente centralizar la creación de conexiones de base de datos y pasarlas a subsistemas en lugar de pasar parámetros de conexión y hacer que los subsistemas establezcan conexiones salientes.

NODE_ENV

Esta es otra idea tentadora pero terrible de Rails. Debe haber exactamente 1 lugar en su aplicación, app/config.jsque observe la NODE_ENVvariable de entorno. Todo lo demás debería tomar una opción explícita como argumento de constructor de clase o parámetro de configuración del módulo.

Si el módulo de correo electrónico tiene una opción sobre cómo entregar correos electrónicos (SMTP, iniciar sesión en stdout, poner en cola, etc.), debería tomar una opción como, {deliver: 'stdout'}pero no debería comprobarlo NODE_ENV.

Pruebas

Ahora mantengo mis archivos de prueba en el mismo directorio que su código correspondiente y uso convenciones de nomenclatura de extensión de nombre de archivo para distinguir las pruebas del código de producción.

  • foo.js tiene el código del módulo "foo"
  • foo.tape.js tiene las pruebas basadas en nodos para foo y vive en el mismo directorio
  • foo.btape.js se puede usar para pruebas que deben ejecutarse en un entorno de navegador

Uso globs del sistema de archivos y el find . -name '*.tape.js'comando para obtener acceso a todas mis pruebas según sea necesario.

Cómo organizar el código dentro de cada .jsarchivo de módulo

El alcance de este proyecto se trata principalmente de dónde van los archivos y directorios, y no quiero agregar mucho otro alcance, pero solo mencionaré que organizo mi código en 3 secciones distintas.

  1. El bloque de apertura de CommonJS requiere llamadas a dependencias de estado
  2. Bloque de código principal de JavaScript puro. No hay contaminación CommonJS aquí. No haga referencia a exportaciones, módulos o requisitos.
  3. Bloque de cierre de CommonJS para establecer exportaciones
Peter Lyons
fuente
1
¿Qué debo usar en lugar de bodyParser si solo tengo algunas rutas que lo usen?
Ilan Frumer
3
Encontré lo que estaba buscando aquí: stackoverflow.com/questions/12418372/…
Ilan Frumer
1
@wlingke eche un vistazo a gist.github.com/branneman/8048520 para una discusión exhaustiva de los enfoques disponibles para ese problema.
Peter Lyons
@peterLyons Gracias por compartir eso. Después de leerlo, creo que escribiré un script de inicio. ¡Gracias!
wlingke
2
con respecto al truco del enlace simbólico de la aplicación , existe este pequeño módulo que soluciona todos los problemas
Hayko Koryun
157

ACTUALIZACIÓN (2013-10-29) : consulte también mi otra respuesta que tiene JavaScript en lugar de CoffeeScript por demanda popular, así como un repositorio de github repetitivo y un extenso README que detalla mis últimas recomendaciones sobre este tema.

Config

Lo que estás haciendo está bien. Me gusta tener mi propio espacio de nombres de configuración configurado en un config.coffeearchivo de nivel superior con un espacio de nombres anidado como este.

#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}"

Esto es amigable para la edición de administrador de sistemas. Luego, cuando necesito algo, como la información de conexión de DB, es

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

Rutas / Controladores

Me gusta dejar mis rutas con mis controladores y organizarlas en un app/controllerssubdirectorio. Luego puedo cargarlos y dejar que agreguen las rutas que necesiten.

En mi app/server.coffeearchivo coffeescript hago:

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

Entonces tengo archivos como:

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

Y, por ejemplo, en mi controlador de dominios, tengo una setupfunción como esta.

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

Puntos de vista

Poner vistas se app/viewsestá convirtiendo en el lugar habitual. Lo expongo así.

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

Archivos estáticos

Ir en un publicsubdirectorio.

Github / Semver / NPM

Ponga un archivo README.md markdown en su raíz de repositorio de git para github.

Coloque un archivo package.json con un número de versión semántica en su raíz de repositorio git para NPM.

Peter Lyons
fuente
1
Hola Peter! Realmente me gusta este enfoque que estás buscando. Estoy trabajando en la construcción de un proyecto exprés y realmente quiero hacer las cosas de la manera correcta que simplemente hackearlo y ponerlo en práctica. Sería excelente si tuviera un repositorio de muestra en github y / o una publicación de blog sobre él.
suVasH .....
44
Este repositorio tiene un montón de patrones que puede usar como referencia: github.com/focusaurus/peterlyons.com
Peter Lyons
75
La secuencia de comandos de Coffee hace que esto sea difícil de leer: / ¿Alguna posibilidad de obtener una edición JS de vainilla? Gracias
toasted_flakes
1
Gracias por esta respuesta Solo estoy tratando de entenderlo. ¿Cómo se accede a los otros controladores dentro de otro (por ejemplo, en la función de configuración como la anterior)app.put route, api.needId
Chmanie
@PeterLyons: hey man, vi tu código fuente pero no tengo idea de cómo hacer el modo de compilación, ya instalé Goe incluí el binarchivo en la estructura. ¿Cómo ejecuta ese goarchivo bin?
user2002495
51

La siguiente es la respuesta literal de Peter Lyons, portada a vanilla JS de Coffeescript, según lo solicitado por varios otros. La respuesta de Peter es muy capaz, y cualquiera que vote por mi respuesta también debería votar por la suya.


Config

Lo que estás haciendo está bien. Me gusta tener mi propio espacio de nombres de configuración configurado en un config.jsarchivo de nivel superior con un espacio de nombres anidado como este.

// 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}"
};

Esto es amigable para la edición de administrador de sistemas. Luego, cuando necesito algo, como la información de conexión de DB, es

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

Rutas / Controladores

Me gusta dejar mis rutas con mis controladores y organizarlas en un app/controllerssubdirectorio. Luego puedo cargarlos y dejar que agreguen las rutas que necesiten.

En mi app/server.jsarchivo javascript hago:

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

Entonces tengo archivos como:

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

Y, por ejemplo, en mi controlador de dominios, tengo una setupfunción como esta.

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);
  });
}

Puntos de vista

Poner vistas se app/viewsestá convirtiendo en el lugar habitual. Lo expongo así.

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

Archivos estáticos

Ir en un publicsubdirectorio.

Github / Semver / NPM

Ponga un archivo README.md markdown en su raíz de repositorio de git para github.

Coloque un archivo package.json con un número de versión semántica en su raíz de repositorio git para NPM.

dthree
fuente
43

Mi pregunta fue presentada en abril de 2011, es bastante antigua. Durante este tiempo, pude mejorar mi experiencia con Express.js y cómo diseñar una aplicación escrita usando esta biblioteca. Entonces, comparto aquí mi experiencia.

Aquí está mi estructura de directorios:

├── 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

El objetivo del app.jsarchivo es arrancar la aplicación expressjs. Carga el módulo de configuración, el módulo de registro, espera la conexión de la base de datos, ... y ejecuta el servidor 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();
});

rutas /

El directorio de rutas tiene un index.jsarchivo. Su objetivo es introducir un tipo de magia para cargar todos los demás archivos dentro del routes/directorio. Aquí está la implementación:

/**
 * 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);
  });
};

Con ese módulo, crear una nueva definición de ruta e implementación es realmente fácil. Por ejemplo hello.js:

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

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

Cada módulo de ruta es independiente .

Sandro Munda
fuente
¿Utiliza un generador para crear esta estructura?
Ashish
18

Me gusta usar una "aplicación" global, en lugar de exportar una función, etc.

tjholowaychuk
fuente
Opto por seguir los consejos de los creadores :) Por cierto, ¿podría proporcionarnos algún código?
Encarnado el
Así es. en estas aplicaciones que ves - github.com/visionmedia/screenshot-app
diproart
17

Creo que es una excelente forma de hacerlo. No se limita a express, pero he visto bastantes proyectos de node.js en github que hacen lo mismo. Sacan los parámetros de configuración + módulos más pequeños (en algunos casos, cada URI) se factorizan en archivos separados.

Recomendaría pasar por proyectos expresos específicos en github para tener una idea. OMI, la forma en que lo estás haciendo es correcta.

neebz
fuente
16

ahora es finales de 2015 y después de desarrollar mi estructura durante 3 años y en proyectos pequeños y grandes. ¿Conclusión?

No haga un MVC grande, sino sepárelo en módulos.

Entonces...

¿Por qué?

  • Por lo general, uno funciona en un módulo (por ejemplo, Productos), que puede cambiar de forma independiente.

  • Puede reutilizar módulos

  • Puedes probarlo por separado

  • Puede reemplazarlo por separado

  • Tienen interfaces claras (estables)

    A más tardar, si había varios desarrolladores trabajando, la separación de módulos ayuda

El proyecto nodebootstrap tiene un enfoque similar a mi estructura final. ( github )

¿Cómo se ve esta estructura?

  1. Pequeños módulos encapsulados , cada uno con MVC separado

  2. Cada módulo tiene un paquete.json

  3. Pruebas como parte de la estructura (en cada módulo)

  4. Configuración global , bibliotecas y servicios

  5. Integrado Docker, Cluster, para siempre

Folderverview (ver la carpeta lib para los módulos):

estructura de nodobootstrap

Simon Fakir
fuente
3
Sería útil si pudiera actualizar la imagen de descripción general de la carpeta con los módulos individuales expandidos también, como un ejemplo de cómo los estructuraría también.
youngrrrr
8

Estoy dando la estructura de carpetas de estilo MVC, por favor encuentre a continuación.

Utilizamos la siguiente estructura de carpetas para nuestras aplicaciones web grandes y medianas.

 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

He creado un módulo npm para la generación de estructurador de carpetas express mvc.

Encuentre el siguiente https://www.npmjs.com/package/express-mvc-generator

Solo pasos simples para generar y usar estos módulos.

i) instalar el módulo npm install express-mvc-generator -g

ii) verifique las opciones express -h

iii) Generar estructura express de mvc express myapp

iv) Instalar dependencias npm install::

v) Abra su config / database.js, configure su mongo db.

vi) Ejecute la aplicación node apponodemon app

vii) Compruebe la URL http: // localhost: 8042 / signup O http: // yourip: 8042 / signup

Raja Rama Mohan Thavalam
fuente
7

Ha pasado bastante tiempo desde la última respuesta a esta pregunta y Express también lanzó recientemente la versión 4, que agregó algunas cosas útiles para organizar la estructura de su aplicación.

A continuación se muestra una publicación de blog larga y actualizada sobre las mejores prácticas sobre cómo estructurar su aplicación Express. http://www.terlici.com/2014/08/25/best-practices-express-structure.html

También hay un repositorio de GitHub que aplica los consejos del artículo. Siempre está actualizado con la última versión Express.
https://github.com/terlici/base-express

Stefan
fuente
7

No creo que sea un buen enfoque agregar rutas a la configuración. Una mejor estructura podría ser algo como esto:

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...

Por lo tanto, products.js y users.js contendrán todas sus rutas con toda la lógica dentro.

ecdeveloper
fuente
6

Bueno, puse mis rutas como un archivo json, que leí al principio, y en un bucle for en app.js configuré las rutas. Route.json incluye la vista que debe llamarse y la clave de los valores que se enviarán a la ruta.
Esto funciona para muchos casos simples, pero tuve que crear manualmente algunas rutas para casos especiales.

TiansHUo
fuente
6

He escrito una publicación exactamente sobre este asunto. Básicamente hace uso de un routeRegistrarque itera a través de archivos en la carpeta que /controllersllama a su función init. La función inittoma la appvariable express como parámetro para que pueda registrar sus rutas de la manera que desee.

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);
Renato Gama
fuente
5

Esto puede ser de interés:

https://github.com/flatiron/nconf

Configuración jerárquica de node.js con archivos, variables de entorno, argumentos de línea de comandos y fusión de objetos atómicos.

Ulises V
fuente
4

1) Su sistema de archivos de proyecto Express puede ser como:

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

app.js: tu contenedor global de aplicaciones

2) Archivo principal del módulo (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) Conecte el módulo en app.js principal

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

4) lógica de muestra

lib/login
lib/db
lib/config
lib/users
lib/verify
lib/
   /api/ 
   ...
lib/
   /admin/
      /users/
      /settings/
      /groups/
...
  • Lo mejor para probar
  • Lo mejor para la escala
  • Separar depende del módulo
  • Ruta de agrupamiento por funcionalidad (o módulos)

tj dice / muestra en Vimeo una idea interesante de cómo modularizar la aplicación express - Aplicaciones web modulares con Node.js y Express . Potente y simple.

diproart
fuente
4

http://locomotivejs.org/ proporciona una forma de estructurar una aplicación creada con Node.js y Express.

Desde el sitio web:

"Locomotive es un marco web para Node.js. Locomotive admite patrones MVC, rutas RESTful y convenciones sobre la configuración, a la vez que se integra a la perfección con cualquier base de datos y motor de plantillas. Locomotive se basa en Express, conservando la potencia y la simplicidad que esperas del nodo ".

Ben Mordue
fuente
3

Recientemente adopté los módulos como miniaplicaciones independientes.

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

Ahora, para cualquier enrutamiento de módulos (# .js), las vistas (* .ejs), js, css y los activos están uno al lado del otro. el enrutamiento de submódulos se configura en el padre # .js con dos líneas adicionales

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

De esta manera, incluso los submódulos son posibles.

No olvide configurar la vista en el directorio src

app.set('views', path.join(__dirname, 'src'));
zevero
fuente
cualquier enlace a github con tal estructura interesado en ver cómo se cargan las rutas, vistas y modelos
Muhammad Umer
Creo que todo está explicado. Las rutas son solo rutas rápidas clásicas. Las vistas deben cargarse con el prefijo de los nombres de los módulos, los modelos deben cargarse haciendo referencia a la ruta relativa.
zevero
En mi última línea, configuré la vista en el directorio src. De aquí en adelante, todas las vistas son accesibles en relación con el directorio src. Nada sofisticado.
zevero
1

Así es como se ve la mayoría de la estructura de directorios de mi proyecto express.

Normalmente hago un express dirnamepara inicializar el proyecto, perdono mi pereza, pero es muy flexible y ampliable. PD: necesita obtener express-generatoreso (para aquellos que lo buscan sudo npm install -g express-generator, sudo porque lo está instalando a nivel mundial)

|-- 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

Debe preguntarse por qué los archivos .env? ¡Porque funcionan! ¡Uso el dotenvmódulo en mis proyectos (mucho recientemente) y funciona! Pop en estas 2 declaraciones en app.jsowww

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

Y otra línea para configurar rápidamente /bower_componentspara servir contenido estático bajo el recurso/ext

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

Probablemente sea adecuado para las personas que buscan usar Express y Angular juntas, o simplemente expresarse sin esa javascriptsjerarquía, por supuesto.

Nitesh Oswal
fuente
1

Mi estructura expresa 4. https://github.com/odirleiborgert/borgert-express-boilerplate

Paquetes

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

Estructura

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

Una forma sencilla de estructurar su aplicación express:

  • En index.js principal, se debe mantener el siguiente orden.

    Todo el conjunto de aplicaciones debe ser el primero.

    Todo el uso de la aplicación debe ser el segundo.

    seguido de otros apis con sus funciones o route-continue en otros archivos

    Exapmle

    app.use ("/ contraseña", contraseñaApi);

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

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

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

Snivio
fuente
0

La mejor forma de estructura MVC para el proyecto ExpressJs con manillar y 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
Manishkumar Bhavnani
fuente
@ sandro-munda por favor verifique
Manishkumar Bhavnani