express 最佳实践 (一)

第二篇: express 最佳实践(二):中间件

最近,一直在使用 nodejs 做项目,对 nodejs 开发可以说深有体会。

先说说 nodejs 在业务中的脚色,, 在 web同构 方面, nodejs 的优势相对于其他语言来说,可以说非常巨大,基本上算是只有 nodejs 能做,其他语言根本不能做。在传统 web 开发方面,nodejs(必竟时间太短了)的相对于其他语言来说的劣势,已经不是太明显了。

再来说说自己,算了算差不多做了 5 个项目,都是使用 express 做为项目的基础框架,然后再上面进行业务开发工作,一个路由下来,平均 10 个左右的中间件,执行效率都是不错的,开发效率也非常好,是时候总结一下 express 实践经验,同时结合配套自己的想法,来看看理想中 express 项目应该是怎样的规划,以及我是怎么想的。

思想

做一个项目的规划,首先要为项目订一个基调,就是项目架构是应该怎样设计,怎样考虑的,对业务开发是否方便,部署是否方便等要素。

我现在认为一个项目最重要的规划是:核心+插件式开发。一个项目一定要支持这两点,如果不支持这个项目就很难做下去,为什么我这样说。因为现在的任何一个项目都不可能是一个人开发,一定是多人分工开发,如果你的项目支持 核心+插件式开发,你就可以把核心的基础功能如:网络,日志,基础布局等功能,找个高级工程师来完成;业务开发,就可以分给一些初级工程师,并行完成,同时在做项目的时候对代码码进行审查,及时调整坏代码。这样项目,能保证进度,也能保证项目质量,不会大烂,同时人力也得到了充分利用。

这种思想落实到架构上,就是从项目的文件组织结构上考虑怎样达到这样的目标。

我是这样规划文件目录的:

按业务特性而不按分层进行规划

分层最典型的代表就是ruby on rails,app 目录下,就是 controllers, models等目录,当然我不是说,这种规划不好,只是到项目大到一定程度,你想找某个 controller 下的一个方法,就会很麻烦,不知道没有这种体验,在几十个文件中找某个方法是怎样的感觉,搜索也是全文搜索,不能限定到某个目录下面。因此在这个项目规划中,以业务特征为插件名,在 express 中,也就是 subExpress,如果,我有一个购物车的业务,那于购物车相关的所有的项目都规在 passport 这个子应用下了,这样相关的业务在一起,方便查找,同时也方便理解和修改。当然这种规划方式也有不方便的地方,如果想跨 subApplication 调用方法,就没有那么方便。

边界显式说明

这个跟传统的 controller, service , model 没有什么区别。还是刚才的 passport 应用,按分层进行划分,包括 controllers, services, models, views, index.js, router.js。需要说明一下,在项目中我们一再强调, controllers 中负责与 http 的连接工作,只做简单参数的验证,传参和调起 services ,禁止把 rep, res 对像做为参数传递到 services 层;services 层纯业务,没有与 http 相关的任何东西,需要数据请调用 models 中的方法;models 只于数据打交到,其它都不管。这样做的好处是什么呢? 想想看,如果相把一个页面的数据拿出来做接口,只需要简单几行就搞定了,还有如果要把框架改成 koa ,你的业务都不用重写,还有你想增加 websocket 的支持,直接在 controllers 进行调整就行了。 models的作用更明显,原来的项目是一个普通是 web 项目,models 是连接数据库的,现在技术升级要前后端分离,不用数据库了,改成调用后端的接口了,你就只用把 models 用接口实现一遍,其他不用变了。 controllers 和 models 就是你业务的边界,service 则是你业务的核心,边界实现的改变不应该影响你业务。

落实

思想利用 express 的中间件思想,达到我的架构意图,如下图所示:

图中,蓝色的部分代表业务中间件,橙黄色代表 核心中间件。在一个网站项目中核心中间件,应该包括 limiter 限流操作,这个中间件主要是防止爬虫;htaccess 改写 url,这个中间件主要用,改写网站url, 为什么要改呢?因为一但网站线上运行时,路由的规则不应发生变化,但是有时候 seo 的时候,你需要兼容新老 url 的时候,这个中间件就会非常有用,网站的业务都不用变,只用在新url 到达时,变成老的再进行处理就行了;dispatch 不是一个中间件,它的思想是用来整合各个业务线的 subApplication 和主 application 的关系的;auth 鉴权,有些页面和接口需要有用户登录,如果没有用户登录,就需要跳转到登录页面,登录完成后跳回来;clientError 和 serverError 是对错误进行统一的处理,统一显示 404 , 如果是接口的话也会统一 404 的返回码。

业务端在开发时只需要在 dispatch 的地方加入自已的模块,然后就可以开始写自己的业务,不用但心自己的文件被别人改动。

下面是项目的文件夹规划:

简单说明下:

apps 目录下就写各自的业务逻辑,用 dispatch 把 主app 与 业务 连接起来,实现起来也挺简单的:

module.exports = (app) => {
app.use('/passport', require('./apps/passport'));
}

还有 shared 的存在,是为了解决前后端模板公用的问题。

与业务没有关系的代码可以,移到到 modules 中使用。

如果个业务的之间需要共享部分代码,就放到 utils 中,这个文件夹就是用来干这种事的。

总结

这个项目中规划就是,怎样使用 express 最好,另外再加上自己的一些思考,现在项目的结构出来了,后面会再讨论一些有关中间件的处理方法。

该项目的 github

nodejs 实践:express 最佳实践 (一) 项目结构的更多相关文章

  1. nodejs 实践:express 最佳实践 (一)

    express 最佳实践 (一) 最近,一直在使用 nodejs 做项目,对 nodejs 开发可以说深有体会. 先说说 nodejs 在业务中的脚色,, 在 web同构 方面, nodejs 的优势 ...

  2. nodejs 实践:express 最佳实践系列

    nodejs 实践:express 最佳实践系列 nodejs 实践:express 最佳实践(一) 项目结构 nodejs 实践:express 最佳实践(二) 中间件 nodejs 实践:expr ...

  3. nodejs 实践:express 最佳实践(三) express 解析

    nodejs 实践:express 最佳实践(三) express 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固, ...

  4. nodejs 实践:express 最佳实践(二) 中间件

    express 最佳实践(二):中间件 第一篇 express 最佳实践(一):项目结构 express 中最重要的就是中间件了,可以说中间件组成了express,中间件就是 express 的核心. ...

  5. nodejs 实践:express 最佳实践(七) 改造模块 connect2 解析

    nodejs 实践:express 最佳实践(七) 改造模块 connect2 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的 ...

  6. nodejs 实践:express 最佳实践(四) express-session 解析

    nodejs 实践:express 最佳实践(四) express-session 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs ...

  7. nodejs 实践:express 最佳实践(五) connect解析

    nodejs 实践:express 最佳实践(五) connect解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需 ...

  8. nodejs 实践:express 最佳实践(六) express 自省获得所有的路由

    nodejs 实践:express 最佳实践(六) express 自省获得所有的路由 某些情况下,你需要知道你的应用有多少路由,这在 express 中没有方法可以.因此我这边曲线了一下,做成了一个 ...

  9. nodejs 实践:express 最佳实践(八) egg.js 框架的优缺点

    nodejs 实践:express 最佳实践(八) egg.js 框架的优缺点 优点 所有的 web开发的点都考虑到了 agent 很有特色 文件夹规划到位 扩展能力优秀 缺点 最大的问题在于: 使用 ...

随机推荐

  1. MySQL上周新增激活用户在上周下单情况_20161107周一

    上周新增激活用户在上周下单情况 1.上周激活用户明细 #上周激活用户明细 SELECT a.城市,a.用户ID,a.用户名称,b.用户地址,b.联系电话,a.订单日期,c.年周,c.上周一,a.订单I ...

  2. Vue项目屏幕自适应方案

    安装lib-flexible cnpm i lib-flexible -D Vue项目引入 lib-flexible. main.js: import 'lib-flexible/flexible' ...

  3. 并查集基础 模板题 hdu1232 畅通工程

    模板题 引入并查集——一则有趣的故事 为了解释并查集的原理,我将举一个更有趣的例子.话说江湖上散落着各式各样的大侠,有上千个之多.他们没有什么正当职业,整天背着剑在外面走来走去,碰到和自己不是一路人的 ...

  4. Linux 运维面试问题总结

    Linux 运维面试问题总结 1.详细描述mysql主从复制的方式?(类似跟DRBD高可用的协议类型) 答: (1)同步复制(C协议:sync):只有在本地和远程磁盘都确定写入已完成时,主节点才会认为 ...

  5. query builder 在线生成sql

    http://devtools.korzh.com/easyquery/javascript/docs/javascript-query-builder-php

  6. JavaScript高级程序设计学习笔记第四章--变量、作用域和内存问题

    1.变量可能包含两种不同数据类型的值:基本类型值和引用类型值. 基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象. 2.变量复制 如果从一个变量向另一个变量复制基本类型的值,会在 ...

  7. java 原始类与封装类 的区别

    int是java提供的8种原始数据类型之一.Java为每个原始类型提供了封装类,Integer是java为int提供的封装类.int的默认值为0,而Integer的默认值为null,即Integer可 ...

  8. 条款39:明智而审慎的使用private继承

    Use private inheritance judiciously. 如果classes之间的继承关系是private,编译器不会自动将一个derived class对象转换为一个base cla ...

  9. 《Java多线程编程核心技术》读后感(十一)

    方法join的使用 在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程往往将早于子线程结束之前结束.这时,如果主线程想等待子线程执行完之后再结束,比如子线程处理一个数据,主 ...

  10. Spring Security 表达式(Expressions) - hasRole示例

    1.概述 Spring Security使用强大的Spring Expression Language(SpEL)提供各种各样的表达式.大多数这些Security表达式是针对上下文对象(当前经过身份验 ...