Mongoose-based app architecture

2019-01-21 02:58发布

问题:

This is not a concrete app/code question, it's just about common app architecture.

I'm trying to understand proper way to organize my mongoose application. As I'm new to mongoose, that's how I do it now:

core/settings.js

var mongoose = require('mongoose');
exports.mongoose = mongoose;
mongoose.connect('mongodb://localhost/blog');
exports.db = mongoose.connection;

core/models.js

settings = require("./settings");

// post schema
var postSchema = settings.mongoose.Schema({
    header: String,
    author: String,
    text: String
})

//compiling our schema into a Model 
exports.post = settings.mongoose.model('post', postSchema)

core/db-layer.js

settings = require("./core/settings");
models = require("./core/models");

exports.function = createAndWriteNewPost(function(callback) {
    settings.db.on('error', console.error.bind(console, 'connection error:'));
    settings.db.once('open', function callback() {
        new models.post({
            header: 'header',
            author: "author",
            text: "Hello"
        }).save(function(err, post) {
            callback('ok');
        });
    });
});

routes/post.js

db = reqiure("../core/db.js")

exports.get = function(req, res) {
    db.createAndWriteNewPost(function(status){
    res.render('add_material', {
      //blah blah blah        
        });
    });
};

app.js

var post = require ('routes/post.js')
...
app.get('/post', post.get);

So, this code was extremely simplified (even not tested) just to show my current architecture thoughts. It's not a concrete app, just something like creating an abstract blog post. So thats how it works:

app.js --> routes/post.js <--> core/db-layer.js
                                   |
                                   v
                               core/models.js <--> core/settings.js

It seems a bit over superfluous for me. Could you suggest more optimal app structure? Thanks.

回答1:

When I first got into Node.js, Express and Mongoose I struggled with scaling my code. The intention of my answer is to help someone who's working on more than just a simple blog, but to help with an even larger scalable project.

  • I am always connected to the database, I do not open and close connections when needed
  • I use index.js as the root file of a folder, just like we'd do in other languages
  • models are kept in their own documents, and require()d into the models/index.js file.
  • routes are similar to models, each route level has a folder, which has an index.js file in turn. So it's easy to arrange something like http://example.com/api/documents/:id. It also makes more sense when one goes through the file structure.

Here's the structure of what I use:

-- app.js
-- models/
---- index.js
---- blog.js
-- mongoose/
---- index.js
-- routes/
---- index.js
---- blog/index.js
-- public/
-- views/
---- index.{your layout engine} => I use Jade.lang
-- methods/
---- index.js => use if you'd rather write all your functions here
---- blog.js => can store more complex logic here

app.js

var db = require('./mongoose'),
  express = require('express');
// note that I'm leaving out the other things like 'http' or 'path'
var app = express();

// get the routes
require('./routes')(app);
// I just require routes, without naming it as a var, & that I pass (app)

mongoose/index.js

// Mongoose connect is called once by the app.js & connection established
// No need to include it elsewhere
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/blog');

// I have just connected, and I'm not exporting anything from here

models/index.js

// Logic here is to keep a good reference of what's used

// models
Blog = require('./blog');
// User = require('./user');

// exports
exports.blogModel = Blog.blogModel;
// exports.userModel = User.userModel;

models/blog.js

So for every model that you work on you create a model.js document, and add it in the models/index.js above. As an example I've added a User model but commented it out.

// set up mongoose
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;

var BlogSchema = Schema({
  header: {type: String },
  author: {type: String },
  text: {type: String },
  _id: { type: ObjectId } // not necessary, showing use of ObjectId
});

Blog = mongoose.model('Blog', BlogSchema);
// the above is necessary as you might have embedded schemas which you don't export

exports.blogModel = Blog;

routes/index.js

module.exports = function(app) {
  app.get('/', function(req, res) {
    // do stuff
  });
  require('./blog')(app);
  // other routes entered here as require(route)(app);
  // we basically pass 'app' around to each route
}

routes/blog/index.js

module.exports = function(app) {
  app.get('/blog', function(req, res) {
    // do stuff
  });
  require('./nested')(app);
  // this is for things like http://example.com/blog/nested
  // you would follow the same logic as in 'routes/index.js' at a nested level
}

suggested use

  • models: for creating the logic that deals with the documents, i.e. creating, updating, deleting, and searching.
  • routes: minimal coding, only where I need to parse http data, create instances of models, and then I send queries to the relevant model.
  • methods: for the more complex logic that doesn't directly involve models. As an example, I have an algorithms/ folder where I store all the algorithms that I use in my app.

Hope this provides more clarity. This structure is working wonders for me as I find it easy to follow.



回答2:

That's pretty much how I go about it, with a few differences:

  • I don't think you can have the open listener inside your function in the db-layer. What I generally do when using a persistent connection like yours is start the application itself in the db open handler. If you don't want to use persistent connections, use createConnection in the db layer function, and make sure you close it before calling the callback. I am not sure if I am making myself clear. Let me know if you want a code example.
  • This is more of a general node.js tip, but I keep my database connection string and other configuration in a json file and require it wherever it is needed. You probably won't need another settings.js file after that.
  • You can also use schema functions (http://mongoosejs.com/docs/api.html#schema_Schema-method) to code some app functionality into your models itself.