elpis 里程碑一总结
1.项目架构设计

1-1.聚焦buff层
buff分三个层级:
- 接入层: router接口路由分发, router-schema路由规则校验 ,middleware路由中间件
- 业务层: controller处理器, env环境分发,config提取,extend服务拓展,schedule定时任务
- 服务层: service处理器
1-2.而elpis-core就是基于buff层设计的用node.js+koa
2.对于elpis-core的理解
graph TD
运行前磁盘文件 --> 解析器 --> 运行时内存
1.elpis-core是一个自动把文件挂载到koa实例上的一个引擎,也是一个轻量版的egg.js内核,他的设计理念是约定式加载,通过写好的各种loader,从预先约定好的目录结构中读取各种js 文件,按照一定的顺序挂载到由koa 创建的app 实例上面,可通过app.midddleware.${目录}.${文件}访问
- 例如:
js
* app/service
* |
* |-- custom-module
* |
* |-- custom-service.js
* => app.service.customModule.customservice
3.elpis-core的结构
| loader | 说明 |
|---|---|
| server.js | 业务模块的自动加载 |
| extend.js | 自动加载扩展,例:外部日志工具log4js |
| router.js | 引入了一个Koa挂载 extend 到 app 上,这个extend可以用来引入日志工具,先把所有 app/router的文件加载到KoaRouter 下,再将路由注册到 app下 |
| routerSchema.js | 对应router的参数的一个具体校验解释文件, |
| controller.js | 控制器自动加载 |
| config.js | 配置区分 本地/测试/生产环境,通过env环境读取不同文件配置 env.config,然后通过 env.config 覆盖 default.config 加载到 app.config 中 |
| middleware.js | 引入自定义中间件loader,如模板渲染中间件等,让中间件自动加载 |
4.koa内部模型(洋葱圈模型)

| 什么是洋葱圈模型? | 中间件执行流程的形象比喻,通过 next() 让代码先一层层进入,再一层层退出,像切开的洋葱一样。 |
|---|---|
| 为什么是先进后出? | 因为 next() 会暂停当前函数,调用下一个函数,这符合调用栈的后进先出(LIFO) 特性。 |
| 有什么好处? | 每个中间件可以在请求前 和响应后都执行逻辑,实现对称处理,非常适合日志、认证、错误处理等场景。 |
5.相关中间件
| 中间件 | 说明 |
|---|---|
| koa-static | 解决静态资源的加载,可以在app/public目录下自动加载相关的静态资源,如:css、png等。 |
| koa-nunjucks-2 | 用于服务器端渲染 HTML(SSR), 全局中间件中引入了koa-nunjucks-2,挂载到了ctx上,从而使得ctx上有render方法 |
| koa-bodyparser | 用于解析 HTTP 请求体,并将数据挂载到 ctx.request.body ,因为Koa 默认无法直接获取请求体中的 body 数据。 |
| log4js | 为日志工具属于Extend,通过 app.logger.info 记录日志并落地磁盘。 |
| api-params-verify | 参数校验基于 JSON-Schema 和 Ajv,配合中间件使用,确保接口数据安全。 |
| api-sign-verify | 接口签名防止数据篡改。前后端约定 Key,通过 MD5(参数+Key) 校验合法性。 |
| error-handler | 对一些异常的报错进行处理,避免用户请求服务出问题,返回一些不必要的内容 |
6.SSR
BFF部署在服务器内网,向后端多个服务发起请求延迟极低,甚至可以并行请求。这远比在浏览器端一个一个请求后端API要快得多,所以BFF层提供了一个完美的地方来做SSR所需要的数据准备工作
1.当前项目提供SSR,在router中调用controller中方法,在controller中写了renderPage方法,因为引入了koaNunjucks,在ctx中可以拿到render方法,所以可以直接在浏览器中输入路由地址渲染出所对应的页面
js
//middleware
module.exports = (app)=>{
// 配置静态根目录
const koaStatic = require('koa-static');
app.use(koaStatic(path.resolve(process.cwd(), `./app/public`)));
// 模板渲染引擎
const koaNunjucks = require('koa-nunjucks-2');
app.use(koaNunjucks({
ext: 'tpl',
path: path.resolve(process.cwd(), `./app/public`),
nunjucksConfig: {
noCache:true,
trimBlocks: true
}
}));
}
js
//controller
module.exports = (app) => {
return class ViewController {
/**
* 渲染页面
* @param {*} ctx 上下文
*/
async renderPage(ctx){
await ctx.render(`dist/entry.${ctx.params.page}`,{
name:app.option?.name,
env:app.env.get(),
option:JSON.stringify(app.option),
});
}
}
}
js
//router
module.exports = (app,router) => {
const {view: ViewController } = app.controller;
// 用户输入http://ip:
router.get('/view/:page',ViewController.renderPage.bind(ViewController))
}
7.为何 Controller 和 Service 要用class?
在 Elpis-Core 中,Controller 和 Service 被设计为 Class(类) ,这带来了极高的可扩展性。
- 复用性 :可以定义一个
BaseController(基类),封装通用方法(如success成功返回、fail错误返回)。 - 继承性 :业务 Controller(如
ProjectController)继承基类,直接复用公共方法。 总结: BFF层级(后端)其实主要由解析引擎 以及业务模块两大板块组成
8.总结
解析引擎elpis-core的主要作用是将各个业务模块聚合在一起并保证其在程序内存中正常运行。这也是例如Eggjs、Nextjs、Nestjs等框架做的事情,也是这些框架设计背后的思想