Elpis-Core 深度理解:企业级 Node.js 框架架构解析
什么是 Elpis-Core?
Elpis-Core 是一个基于 Koa 的 Node.js 内核实现。
分层架构
三层设计:
markdown
┌─────────────────────────────────────┐
│ Controller 层(业务逻辑入口) │
│ - 接收请求 │
│ - 参数校验 │
│ - 调用 Service │
│ - 返回响应 │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Service 层(数据处理) │
│ - 业务逻辑处理 │
│ - 数据库操作 │
│ - API 调用 │
│ - 数据转换 │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Middleware 层(横切关注点) │
│ - 参数校验 │
│ - 权限验证 │
│ - 异常处理 │
│ - 日志记录 │
└─────────────────────────────────────┘
模块化加载
Loader 系统:
- 先扫描目录
- 再加载模块
- 然后挂载到 app
- 支持层级嵌套
启动入口(index.js)
核心流程:
javascript
const Koa = require("koa");
const path = require("path");
const { sep } =path;
const env = require("./env");
const middlewareLoader = require("./loader/middleware");
const routerSchemaLoader = require("./loader/router-schema");
const routerLoader = require("./loader/router");
const controllerLoader = require("./loader/controller");
const serviceLoader = require("./loader/service");
const configLoader = require("./loader/config");
const extendLoader = require("./loader/extend");
module.exports = {
/**
* 启动项目
* @params options 项目配置
* options = {
* name 项目名称
* homePath 项目首页
* }
*/
start(options) {
// 实例化
const app = new Koa();
// 应用配置
app.options = options;
// 项目基础路径
app.baseDir = process.cwd();
// 业务文件路径
app.businessPath = path.resolve(app.baseDir, `.${sep}app`);
// 初始化环境变量
app.env = env();
console.log(`-- [start] env: ${app.env.get()} --`);
// 加载配置 config
configLoader(app);
console.log(`-- [start] load config done --`);
// 加载扩展 extend
extendLoader(app);
console.log(`-- [start] load extend done --`);
// 加载中间件 middleware
middlewareLoader(app);
console.log(`-- [start] load middleware done --`);
// 加载路由 routerSchema
routerSchemaLoader(app);
console.log(`-- [start] load routerSchema done --`);
// 加载服务 service
serviceLoader(app);
console.log(`-- [start] load service done --`);
// 加载控制器 controller
controllerLoader(app);
console.log(`-- [start] load controller done --`);
// 注册全局中间件
// app/middlewareLoader.js
try{
require(`${app.businessPath}${sep}middleware.js`)(app);
console.log(`-- [start] load global middleware done --`);
}catch(e){
console.log(`[exception] there is no global middleware file`)
}
// 加载路由 router (因为需要经过中间件所以路由分发在最后)
routerLoader(app);
console.log(`-- [start] load router done --`);
// 启动服务
try {
const port = process.env.PORT || 8080;
const host = process.env.IP || "0.0.0.0";
app.listen(port, host);
console.log(`Server running on port: ${port}`);
} catch (e) {
console.log(e);
}
},
};
关键点:
app对象是整个框架的核心- 所有模块都挂载到
app上 - Loader 按顺序加载,有依赖关系
- 路由最后加载,确保中间件已注册
Loader 加载机制
Loader 系统概览
Elpis-Core 提供了 7 个 Loader,每个负责加载特定类型的模块:
| Loader | 功能 | 输出 | 依赖 |
|---|---|---|---|
| configLoader | 加载配置 | app.config |
env |
| extendLoader | 加载扩展 | 扩展 app | 无 |
| middlewareLoader | 加载中间件 | app.middlewares |
无 |
| routerSchemaLoader | 加载校验规则 | app.routerSchema |
无 |
| serviceLoader | 加载服务 | app.service |
config |
| controllerLoader | 加载控制器 | app.controller |
service |
| routerLoader | 加载路由 | 注册路由 | controller, routerSchema |
Loader 工作原理
通用加载流程:
javascript
// 1. 扫描目录
const fileList = glob.sync(path.resolve(directory, '**/**.js'));
// 2. 遍历文件
fileList.forEach(file => {
// 3. 提取文件名
let name = extractFileName(file);
// 4. 转换命名(驼峰)
name = name.replace(/[_-][a-z]/ig, s => s.substring(1).toUpperCase());
// 5. 加载模块
const module = require(file)(app);
// 6. 挂载到 app
app[category][name] = module;
});
1. Middleware Loader
中间件,采用洋葱圈模型,实现请求参数校验、异常捕获等功能,可以横向拓展。

2. Controller Loader
请求控制,进行http统一处理,api响应等业务处理能力。
3. Service Loader
服务层,负责主要业务逻辑,对数据进行处理,实现数据的获取返回等等。
4. Config Loader
配置层,先行加载,不同配置文件加载不同环境配置信息等。
5. Router Schema Loader
对router进行校验,通过加载json-schema对api进行规则约束。
6. Extend Loader
加载extend模块,将其挂载到app示例,实现横向功能扩展。
7. Router Loader
加载路由模块,根据router-schema动态生成路由并挂载到koa实例上,统一管理。
中间件执行流程
scss
┌─────────────────────────────┐
│ 请求进入 │
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ koa-static (静态文件) │ ← 第一层
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ koa-nunjucks (模板引擎) │ ← 第二层
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ bodyParser (请求解析) │ ← 第三层
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ errorHandler (异常处理) │ ← 第四层
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ apiSignVerify (签名校验) │ ← 第五层
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ apiParamsVerify (参数校验) │ ← 第六层
│ - 校验 headers │
│ - 校验 query │
│ - 校验 body │
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ koa-router (路由匹配) │ ← 第七层
│ - ctx.params 注入 │
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ paramsValidator (参数校验) │ ← 第八层
│ - 校验 params │
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ Controller (业务逻辑) │ ← 第九层
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ 响应返回 │
└─────────────────────────────┘
中间件注册顺序
javascript
// app/middleware.js
module.exports = (app) => {
// 1. 静态文件服务
app.use(koaStatic('./app/public'));
// 2. 模板渲染引擎
app.use(koaNunjucks({ ext: 'tpl', path: './app/public' }));
// 3. 请求体解析
app.use(bodyParser());
// 4. 异常捕获
app.use(app.middlewares.errorHandler);
// 5. 签名校验
app.use(app.middlewares.apiSignVerify);
// 6. 参数校验(headers/query/body)
app.use(app.middlewares.apiParamsVerify);
};
params 校验的特殊处理
问题:params 在路由匹配后才注入,无法在路由前校验。
解决方案:
javascript
// elpis-core/loader/router.js
const { createParamsValidator } = require('../middleware/validate');
module.exports = (app) => {
const router = new KoaRouter();
// 在路由内部注册 params 校验中间件
router.use(createParamsValidator(app));
// 注册路由...
};
执行时机:
csharp
请求 → apiParamsVerify → koa-router → paramsValidator → Controller
↑ 校验headers等 ↑ 注入params ↑ 校验params
总结
搭建内核引擎不是要去理解业务需求,而是对程序的架构思维理解,如何通过统一约束,通过同种方式加载不同功能,理解生命周期去优化执行顺序,是应用页面开发到架构设计的过渡。