我正在寻找最简单的&用于管理项目的多租户express.js应用程序的高效方法。
阅读几篇博客和文章,我发现,对于我的应用程序,每个租户架构都有一个数据库会很好。
我的第一次尝试是使用子域来检测租户,然后将子域映射到mongodb数据库。
我想出了这个快速的中间件
var mongoose = require('mongoose');
var debug = require('debug')('app:middleware:mongooseInstance');
var conns [];
function mongooseInstance (req, res, next) {
var sub = req.sub = req.subdomains[0] || 'app';
// if the connection is cached on the array, reuse it
if (conns[sub]) {
debug('reusing connection', sub, '...');
req.db = conns[sub];
} else {
debug('creating new connection to', sub, '...');
conns[sub] = mongoose.createConnection('mongodb://localhost:27017/' + sub);
req.db = conns[sub];
}
next();
}
module.exports = mongooseInstance;
然后我在另一个中间件中注册模型:
var fs = require('fs');
var debug = require('debug')('app:middleware:registerModels');
module.exports = registerModels;
var models = [];
var path = __dirname + '/../schemas';
function registerModels (req, res, next) {
if(models[req.sub]) {
debug('reusing models');
req.m = models[req.sub];
} else {
var instanceModels = [];
var schemas = fs.readdirSync(path);
debug('registering models');
schemas.forEach(function(schema) {
var model = schema.split('.').shift();
instanceModels[model] = req.db.model(model, require([path, schema].join('/')));
});
models[req.sub] = instanceModels;
req.m = models[req.sub];
}
next();
}
然后我可以像任何其他express.js app一样正常进行:
var express = require('express');
var app = express();
var mongooseInstance = require('./lib/middleware/mongooseInstance');
var registerModels = require('./lib/middleware/registerModels');
app.use(mongooseInstance);
app.use(registerModels);
app.get('/', function(req, res, next) {
req.m.Project.find({},function(err, pets) {
if(err) {
next(err);
}
res.json({ count: pets.length, data: pets });
});
});
app.get('/create', function (req, res) {
var p = new req.m.Project({ name: 'Collin', description: 'Sad' });
p.save(function(err, pet) {
res.json(pet);
});
});
app.listen(8000);
该应用程序工作正常,我现在没有更多,我想在继续之前得到一些反馈,所以我的问题是:
这种方法是否有效?考虑到这里会发生很多事情,多个租户,每个都有几个用户,我打算设置webhooks以触发每个实例,电子邮件,等...
我是否缺少任何瓶颈/陷阱?我正试图从一开始就让它具有可扩展性。
模型注册怎么样?我没有找到任何其他方法来实现这一目标。
谢谢!
答案 0 :(得分:6)
这种方法有效吗? 我有什么瓶颈/缺陷吗?
这一切似乎对我来说都是正确的
模型注册怎么样?
我同意@ narc88您不需要在中间件中注册模型。
由于缺乏更好的术语,我会使用工厂模式。这个“工厂函数”会占用您的子域,或者您决定检测租户,并返回Models
个对象。如果给定的中间件想要使用其可用的Models
,则只需执行
var Models = require(/* path to your Model factory */);
...
// later on inside a route, or wherever
var models = Models(req.sub/* or req.tenant ?? */);
models.Project.find(...);
对于“工厂”示例,请原谅复制/粘贴
var mongoose = require('mongoose');
var fs = require('fs');
var debug = require('debug')('app:middleware:registerModels');
var models = [];
var conns = [];
var path = __dirname + '/../schemas';
function factory(tenant) {
// if the connection is cached on the array, reuse it
if (conns[tenant]) {
debug('reusing connection', tenant, '...');
} else {
debug('creating new connection to', tenant, '...');
conns[tenant] = mongoose.createConnection('mongodb://localhost:27017/' + tenant);
}
if(models[tenant]) {
debug('reusing models');
} else {
var instanceModels = [];
var schemas = fs.readdirSync(path);
debug('registering models');
schemas.forEach(function(schema) {
var model = schema.split('.').shift();
instanceModels[model] = conns[tenant].model(model, require([path, schema].join('/')));
});
models[tenant] = instanceModels;
}
return models[tenant];
}
module.exports = factory;
除了潜在的(虽然可能很小)性能提升,我认为它还具有以下优势: