Search code examples
node.jsexpressmongoosedaoseparation-of-concerns

Separating DAOs from controllers for reusability purposes


I want to make available a list of categories in several views: visualize them as a list, load them as options to the create product form ... In order to reuse these categories, I have to separate the DAO from controllers. The problem is how to deliver the returned categories in categoryDao to the categoryController.

This is my MVC literate code with my issue:

// categoryDao.js
var Category = mongoose.model('Category');
exports.allCategories = function() {
    Category.find().exec(function(err, categories) {
        console.log(categories); // is properly defined

        // I don't want to render categories like:
        res.render('categories', {allCategories : categories});

        // insted, I want to return them from within this callback and make them available in the categoryController.js. But you know
        return categories; // is not quite right
    }
    // and of course
    console.log(categories); // is undefined
}

// categoryController.js
var categoryDao = require ('./categoryDao.js');
exports.all = function(req, res) {
    // make allCategories available in a list view
    res.render('categories', {allCategories : categoryDao.allCategories});
}

// productController.js
var categoryDao = require ('./categoryDao.js');
exports.createForm = function(req, res) {
    // make allCategories available in a select element in the create product form
    res.render('create', {allCategories : categoryDao.allCategories});
}

// categoryRoutes.js
var categoryController = require ('./categoryController.js');
app.route('/categories').get(categoryController.all);

Solution

  • You can't return an asynchronous result from a method, you need to use callbacks instead.

    // categoryDao.js
    var Category = mongoose.model('Category');
    exports.allCategories = function(callback) {
        Category.find().exec(function(err, categories) {
            console.log(categories);
    
            // Deliver the results to the caller via the callback they provided
            callback(err, categories);
        }
    }
    
    // categoryController.js
    var categoryDao = require ('./categoryDao.js');
    exports.all = function(req, res) {
        // make allCategories available in a list view
        categoryDao.allCategories(function(err, categories) {
            res.render('categories', {allCategories : categories});
        });
    }
    
    // productController.js
    var categoryDao = require ('./categoryDao.js');
    exports.createForm = function(req, res) {
        // make allCategories available in a select element in the create product form
        categoryDao.allCategories(function(err, categories) {
            res.render('create', {allCategories : categories});
        });
    }
    
    // categoryRoutes.js
    var categoryController = require ('./categoryController.js');
    app.route('/categories').get(categoryController.all);