我正在尝试了解如何使用Node / Express / Mongo(实际上使用MEAN堆栈)构建企业应用程序。
在阅读了2本书和一些谷歌搜索(包括类似的StackOverflow问题)之后,我找不到使用Express构建大型应用程序的任何好例子。我读过的所有资料都建议通过以下实体拆分应用程序:
但我看到这个结构的主要问题是控制器就像上帝对象,他们知道req
,res
个对象,负责验证,有业务逻辑包括在内。
另一方面,我认为路由过度工程,因为他们所做的只是将端点(路径)映射到控制器方法。
我有Scala / Java背景,所以我习惯将3层中的所有逻辑分开 - controller / service / dao。
对我来说,以下陈述是理想的:
控制器只负责与WEB部分交互,即编组/解组,一些简单的验证(必需,最小,最大,电子邮件正则表达等);
服务层(实际上我错过了NodeJS / Express应用程序)只负责业务逻辑,一些业务验证。服务层对WEB部分一无所知(即可以从其他应用场所调用它们,而不仅仅是从Web上下文中调用);
关于DAO层对我来说很清楚。 Mongoose模型实际上是DAO,所以这里对我来说最清楚。
我认为我看过的例子非常简单,它们只显示了Node / Express的概念,但我想看看一些真实世界的例子,其中涉及大部分业务逻辑/验证。
编辑:
另一件事我不清楚是否缺少DTO对象。考虑这个例子:
const mongoose = require('mongoose');
const Article = mongoose.model('Article');
exports.create = function(req, res) {
// Create a new article object
const article = new Article(req.body);
// saving article and other code
}
来自req.body
的JSON对象作为参数传递,用于创建Mongo文档。它闻起来对我不好。我想使用具体的类,而不是原始的JSON
感谢。
答案 0 :(得分:4)
每个人都有自己的方式将项目划分到某些文件夹中。 我使用的结构是
配置文件夹包含配置文件,例如所有开发阶段的数据库连接设置,如"生产","开发""测试"
例如
'use strict'
var dbsettings = {
"production": {
//your test settings
},
"test": {
},
"development": {
"database": "be",
"username": "yourname",
"password": "yourpassword",
"host": "localhost",
"connectionLimit": 100
}
}
module.exports = dbsettings
日志文件夹包含用于调试的连接日志错误日志
controller用于验证您的req数据和业务逻辑
例如
const service = require("../../service")
const async = require("async")
exports.techverify = (data, callback) => {
async.series([
(cb) => {
let searchObject = { accessToken: data.accessToken }
service.admin.get(searchObject, (err, result) => {
if (err || result.length == 0) {
callback(err, { message: "accessToken is invalid" })
} else {
delete data.accessToken
service.tech.update(data, { verified: true }, (err, affe, res) => {
if (!err)
callback(err, { message: "verification done" })
else
callback(err, { message: "error occured" })
})
}
})
}
])
}
模型用于定义数据库模式
示例mongoDb架构
'use strict'
let mongoose = require('mongoose');
let schema = mongoose.Schema;
let user = new schema({
accesstoken: { type: String },
firstname: { type: String },
lastname: { type: String },
email: { type: String, unique: true },
image: { type: String },
phoneNo: { type: String },
gender: { type: String },
deviceType: { type: String },
password: { type: String },
regAddress: { type: String },
pincode: { type: String },
fbId: { type: String, default: 0 },
created_at: { type: Date, default: Date.now },
updated_at: { type: Date, default: Date.now },
one_time_password: { type: String },
forgot_password_token: { type: String },
is_block: { type: Boolean, default: 0 },
skin_type: { type: String },
hair_length: { type: String },
hair_type: { type: String },
credits: { type: Number, default: 0 },
invite_code: { type: String },
refered_by: { type: String },
card_details: [{
card_type: { type: String },
card_no: { type: String },
card_cv_no: { type: String },
created_at: { type: Date }
}]
});
module.exports = mongoose.model('user', user);
服务用于编写数据库查询避免在控制器中编写查询尝试在此文件夹中编写查询并在控制器中调用
使用mongoose查询
'use strict'
const modelUser = require('../../models/user');
exports.insert = (data, callback) => {
console.log('mongo log for insert function', data)
new modelUser(data).save(callback)
}
exports.get = (data, callback) => {
console.log('mongo log for get function', data)
modelUser.find(data, callback)
}
exports.update = (data, updateData, callback) => {
console.log('mongo log for update function', data)
modelUser.update(data, updateData, callback);
}
exports.getWithProjection = (data, projection, callback) => {
console.log('mongo log for get function', data)
modelUser.find(data, projection, callback)
}
utils是用于你的项目中常用的常用工具函数,可能像加密,解密密码等
例如
exports.checkPassword = (text, psypherText) => {
console.log("checkPassword executed")
console.log(text, psypherText)
return bcrypt.compareSync(text, psypherText)
}
exports.generateToken = (userEmail) => {
return jwt.sign({ unique: userEmail, timeStamp: Date.now }, config.keys.jsonwebtoken)
}
答案 1 :(得分:0)
rohit salaria的答案基本上解释了您在java中习惯的相同应用程序结构。
我有几点评论。第一个也是最重要的一个是这不是Java。这可能听起来很明显,但只要看看你的问题,看看你是否正在寻找与你在Java世界中使用的相同概念相同的开发体验。以下是对此的解释。
缺少DTO。在Java中,它们只是必需的时期。在Java Web应用程序中,您将数据存储在关系数据库中,并以JSON方式向前端发送和接收数据,将数据转换为Java对象是很自然的。但是在Node应用程序中,一切都是javascript和JSON。这是该平台的优势之一。由于JSON是常见的数据格式,因此不需要编写代码或依赖库来在层的数据格式之间进行转换。
将数据对象直接从请求传递到模型。为什么不?将JSON作为从前端到数据库的通用数据格式,您可以轻松地在所有层之间同步应用程序的数据模型。当然你不必走这条路,但大部分时间都足够了,为什么不用呢?至于验证,它是在模型中完成的,它根据MVC理论属于它(而不是在懒惰和实用主义经常提出的控制器中)。)。
对于我想要添加的最终想法,在项目规模扩展方面,这不是最佳平台。根本就是点头棒,但Java在这方面更好。
答案 2 :(得分:0)
答案 3 :(得分:-1)
我已经完成了你想要做的事情。基本上,传统的路径/控制器/模型结构中缺少一层。简短的回答是,这在节点领域还没有发展到你想要它的方式 - 所以如果你想操纵对象,就会有自定义的事情要做。
一些建议开始于:
我发现实现此目标的最有效方法是让对象具有访问模型的静态方法,然后将它们导入控制器。现在 - 这需要花费更多的时间来设置,而不仅仅是关注节点服务器上的文档 - 但是一旦你完成了它就太简单了,大型团队的分工也很棒(一旦团队可以字面上专用于路由/控制器,而另一个管理DAO /模型。)
// controller
import Article from 'models/Article';
export ArticleController {
class GET {
handler( req, res ){
return Article.find(req.params.id);
}
}
class POST {
validator: {
// this is where you ensure req.payload is going to be sufficient for the article constructor
payload: {
name: joi.string().required()
}
}
handler( req, res ){
const oArticle = new Article(req.payload);
oArticle.save();
}
}
}
//Article
export class Article {
public id: string;
public name: string;
constructor(data){
// over-simplified logic to load data into object for example
// there are some edge cases you need to figure out
Object.assign(this, data);
}
public static find( id ){
// get the article from your DAO - pseudo code
const data = DAO.getArticleDataById(id);
return new Article(data);
}
public save(){
// save this object using DAO
}
}