1. 组件树形结构
- 树干=框架
- 叶子=业务逻辑
这种设计结构需要设计者在设计时最好将业务逻辑点能够很好的规划出来,不能让叶子逻辑点之间有逻辑关联,否则这种类型的设计结构将不太适合.
树干这里要做的事情就是为叶子提供管道,当逻辑触发点触发时,框架是不做任何业务逻辑相关的操作,只负责能够将该消息能够路由到正确的叶子结点上.对于叶子需要使用的第三方库,都整合到树干中做成树干的工具,业务逻辑点进行调用.例如数据库,redis,操作等都不要叶子结点自己去建立连接使用.
场景举例:
场景需求中各个逻辑点之间无逻辑关联,满足树形结构设计.
消息设计方式:
设置玩家头像: {path:"setting/user/avator"}
设置用户名字: {path:"setting/user/name"}
校验用户: {path:"auth/user"}
服务器目录设置结构:
setting
–>user
–>avator.cpp
–>name.cpp
auth
–>user.cpp
这种设计方式参考了一定的 rest api 不过这个主要是 web 方面的设计,我觉得这套设计风格不一定局限于web方面,在我们做其他服务器时也可以参考类似的风格.
- 优点
1).结构清楚
项目是随着业务进行变动的,时间长必然会让代码越来越庞大,如果前期没有一个好的结构,后期维护的时候会很心累,因为加一个逻辑或者减少一个逻辑都有可能对其他功能造成影响.而且年龄增长,长时间从事这种编程工作在记忆力方面肯定会比刚工作时下降很多,有时候正在修改代码时突然一个电话、项目经理谈话、线上故障等会中断当前思维,在回来看时有可能忘记了当时正在处理的事情,从而埋下线上隐患.所以对于业务逻辑来说,划分越小越好,越细越好.还有对于程序维护来说一个最大的问题莫过于负责人离职,如果代码没有一个清晰的结构对于交接人来说是一种灾难,可能会出现交接人不理解这段代码为什么要这么设计,然后进行修改,结果引发大事故情况.
2).方便复用
因为叶子都是和该项目相关的逻辑,只要将叶子摘除或者替换,其他不变就可以直接编写其他项目.
3).方便扩展
使用这种结构无论是增加业务还是删除业务,都不会对其他逻辑造成影响,因为逻辑相互独立,所以每次的版本迭代都只需要测试新需求即可,其他功能测试不需要花费较多经历去测试.而且这样的结构也容易构造成代码模版,如果有新的需求可以使用工具待写一部分代码.
Promise.all(jobs)
.then(Sort)
.then(Limit)
.then(function(result){
global.logger.info("%s | 获取<%bussname%>完成",__file);
res.json(result);
})
.catch(function(err){
global.logger.error("%s | 获取<%bussname%>异常,err:",__file,err);
res.json({
totalCount:0
});
});
- 缺点
1).消息数据包增大
因为每个消息都有明确的路由路径,这样的消息包远比单纯使用数字方式的设计要大,所以使用这种方式,都需要做好协议压缩中间件.
2).主干设计要求较高
主干的设计对开发者要求较高,如果设计层面不成熟,可能做出的设计会不太理想,反而增加维护成本.
2.多组件/模块包装
这种设计主要是为了系统组件模块化,目的在于可复用性.这种多组建包装可以针对整个系统也可以针对某个组件.
多模块包装:
多组件包装:
这种类型设计要求的核心思想是,模块或者组件不能产生任何与业务相关的数据,例如日志服务,如果要可复用,就不能出现依赖业务系统依赖的数据库表,或者redis数据结构表等数据去做逻辑,否则就会造成不可复用性,无论在设计或者需求变更时,尽量要做到数据的源头只有一个去操作控制(业务逻辑/系统),其他只负责中转的角色.这样复用时就可以无缝衔接于其他项目.
3.代码模版设计
代码模版指的是coder在编写代码时最好把各个无关联业务子逻辑代码结构做成相似结构,例如下面两个逻辑
设置用户信息
setting/user.js:
var index = __filename.lastIndexOf("/");
if(index === -1){
index = __filename.lastIndexOf("\\");
}
var thisId = __filename.substr(index + 1,__filename.length - 3 - index -1);
module.exports = function(app){
app.post("/setting/" + thisId,function(req,res){
var param = req.body ;
param.fcadmin = req.fcadmin;
processReq(param,function(data){
res.json(data);
});
});
function processReq(param,cb){
var res = {
code:0,
message:""
};
// 设置用户逻辑
process.nextTick(function(){cb(res);});
}
}
修改用户信息
update/user.js:
var index = __filename.lastIndexOf("/");
if(index === -1){
index = __filename.lastIndexOf("\\");
}
var thisId = __filename.substr(index + 1,__filename.length - 3 - index -1);
module.exports = function(app){
app.post("/update/" + thisId,function(req,res){
var param = req.body ;
param.fcadmin = req.fcadmin;
processReq(param,function(data){
res.json(data);
});
});
function processReq(param,cb){
var res = {
code:0,
message:""
};
// 修改用户逻辑
process.nextTick(function(){cb(res);});
}
}
- 优点
1).代码容易复制
代码容易复制可以很快速的扩展新业务,只需要修改相应的核心逻辑内容即可,减少冗余代码编写.
2).容易做成模版
如果你每次新增加的业务需要增加的文件较多,那么推荐制作一个工具,然后做一个模版代码,这样使用工具即可生成新业务的代码,我们只需要部分新代码部分即可.
# 指定生成的文件名字
name MyExport
# 指定生成业务名字
bussname 我的导出
# 临时文件目录
tmp ./tmp
# 指定模版内容替换内容
content {
group {
# 指定源配置模版
src <%name%>
# 生成目的名字
dest $name
}
group {
# 指定源配置模版
src <%bussname%>
# 替换目的名字
dest $bussname
}
}
# 指定的模版配置目录
template ./template
# 指定模版源目录 目录中需要包含 html文件目录 js文件目录 control 目录
src {
html /views
js /js
control /module
}
# 将 html js control 生成到指定目录
dest{
html ./views
js ./public/js/$name
control ./routes/module
}
类似上面新增一个业务代码时,我需要生成大概6个以上文件,如果每次都手动操作的话容易出错不说还浪费时间,而这样使用工具模版后我们只需要修改 name 和 bussname 字段,运行工具即可.
4.日志输出设计规格
日志是在排查问题,测试功能比较重要的内容.日志不宜输出过多,也不应该输出过少,我觉得日志只需要4个级别即可
- 错误
错误级别表示当前逻辑流无法在继续进行,需要中断返回给客户端,这种级别最好是一直开启,否则出现错误并不知道什么错误导致.
- 警告
警告级别表示当前逻辑流遇到了不符合预期的情况,但是仍然可以继续后续逻辑流.
- 信息
信息表示一个逻辑流正常结束,需要打印一个日志来标记一下,方便排查问题时看是否打印了这条日志来断定逻辑是否正常完成.
- 调试
调试级别是在一个逻辑流正在进行过程中,在开发期间或者查问题期间,看一下是否某个逻辑的数据格式,方便排查问题.
一个逻辑流中,日志输出使用我总结以下几点:
(1). 错误只能出现一条,出现错误当前逻辑流就结束
(2).信息只能在一个逻辑流结束出现一条
(3).信息和错误不能共存
线上的代码,最好开启到信息级别,如果代码已经是很稳定,那么可以开启到警告或者错误,但至少要开启到错误.如果有新的功能加入,代码最好先开启到信息一段时间观测,如果稳定,那么可以考虑开启到错误.线上运行如果是用户较少情况,有时也可以开启到调试级别,但仅仅局限于调试问题期间,否则不建议开启到调试,因为调试的日志量太大,会对IO有影响从而影响系统.