Structure of Express/Mongoose app

后端 未结 5 1380
醉酒成梦
醉酒成梦 2021-02-10 09:00

How should I go about structuring my express/mongoose application, so that I can use my schemas, models, routes and the functions that get called when those routes are hit?

5条回答
  •  悲&欢浪女
    2021-02-10 10:05

    There are more or less two axes to organize your code. Organize code based on the layer functionality of your modules (database, model, external interface) or by functionality/context they act on (users, orders). Most (MVC) applications use a functional organization schema which is easier to handle but does not reveal the purpose or intend of an application.

    Beside organizing code functional layers should be as decoupled as possible.

    The functional layers in your code are

    • Models that abstract data and behavior in your application
    • Routes that constitute an external interface of your application. Routes are not the application!
    • Bootstrapping code (server.js) that is responsible to start and connect the parts of your application

    The code base above seems to use a functional organization schema, which is fine. The use of a modules directory does not really make sense to me and seems superfluous. So we have a schema somehow like this

    |- server.js
    |+ users
     |- schema.js
     |- routes.js
    

    Now let's break some dependencies...

    schema.js

    The schema/model part of the code should not depend on the app that represents an interface of your application. This version of schema.js exports a model and does not require an express app or a mongoose instance to be passed into some kind of factory function:

    var mongoose = require('mongoose');
    var Schema = mongoose.Schema;
    
    var UserSchema = Schema({
        username: { type: String, required: true },
        password: { type: String }
    });
    
    // Use UserSchema.statics to define static functions
    UserSchema.statics.userlist = function(cb) {
        this.find().limit( 20 ).exec( function( err, users ) 
        {
            if( err ) return cb( err );
    
            cb(null, users);
        });
    };
    
    module.exports = mongoose.model( 'User', UserSchema, 'users' );
    

    Obviously this misses the app.send functionality from the original file. This will be done in the routes.js file. You may notice that we do not export /api/v1/users anymore but / instead. This makes the express app more flexible and the route self contained.

    See this post for a article explaining express routers in detail.

    var express = require('express');
    var router = express.Router();
    var users = require('./schema');
    
    // get all users
    router.get( '/', function(req, res, next) {
        users.userlist(function(err, users) {
          if (err) { return next(err); }
    
          res.send(users);
        });
    });
    
    // get one user
    router.get( '/:id', ...);
    
    // add one new user 
    router.post( '/', ...);
    
    module.exports = router;
    

    This code omits implementations for getting one user and creating new users because these should work quite similar to userlist. The userlist route now has a single responsibility to mediate between HTTP and your model.

    The last part is the wiring/bootstrapping code in server.js:

    // setup
    var express = require("express");
    var app = express();
    var mongoose = require("mongoose");
    
    mongoose.connect( 'mydb' ); // Single connection instance does not need to be passed around!
    
    // Mount the router under '/api/v1/users'
    app.use('/api/v1/users', require('./users/routes'));
    
    // listen
    app.listen( 3000 );
    

    As a result the model/schema code does not depend on the application interface code, the interface has a clear responsibility and the wiring code in server.js can decide which version of the routes to mounter under which URL path.

提交回复
热议问题