Elpis 项目阶段性总结 - 里程碑一

0 阅读5分钟

Elpis 介绍

Elpis 是一个基于 Koa.js 的轻量级 Node.js Web 框架,提供了一套优雅的应用程序结构和加载机制。 它不是一个“大而全”的框架,而是一个带有约定目录、自动加载、路由注册、参数校验、模板渲染能力的小型应用骨架。

一、项目整体定位

主要内容:

  • 基于 Koa 搭建应用运行时
  • 通过约定目录自动加载 config / service / controller / middleware / router / router-schema / extend
  • 提供统一的全局中间件机制
  • 支持页面渲染ssr和 API 接口
  • 为 API 增加签名校验、参数校验能力

项目大致目录结构如下

elpis/
├── elpis-core/          # 核心框架层
│   ├── index.js         # 框架启动入口
│   ├── env.js           # 环境判断
│   └── loader/          # 加载器集合
│       ├── config.js    # 配置加载器
│       ├── controller.js # 控制器加载器  
│       ├── service.js   # 服务层加载器
│       ├── middleware.js # 中间件加载器
│       ├── router.js    # 路由加载器
│       ├── router-schema.js # 路由参数校验 Schema 验证加载器
│       └── extend.js    # 扩展加载器
├── app/                 # 应用层
│   ├── controller/      # 控制器层
│   ├── service/         # 服务层  
│   ├── middleware/      # 中间件层
│   ├── router/          # 路由层
│   ├── router-schema/   # 路由参数验证
│   ├── extend/          # 框架扩展
│   └── middleware.js    # 全局中间件配置
└── config/              # 配置层
    ├── config.default.js # 默认配置
    ├── config.local.js   # 本地环境配置
    ├── config.beta.js    # 测试环境配置
    └── config.prod.js    # 生产环境配置

二、启动流程

项目整体流程图如下: elpis-core结构图.png

  • 要理解KOA的洋葱圈模型,请求从外层中间件进入,一层层向内传递,到达核心后再一层层向外返回。
  • 采用分层设计:
  1. Controller: 接收请求params / body / query调用 Service,返回响应,controller基类统一收拢 controller 相关的公共方法success / fail
  2. Service: 业务层,service基类统一收拢 service 相关的公共方法
  3. Router: 路由层,负责把 URL 映射到 Controller 项目入口核心在 elpis-core/index.js

启动时主要做了几件事:

  1. 创建 Koa 实例
  2. 挂载基础信息到 app
  3. 初始化环境变量对象 app.env
  4. 按顺序加载各类资源
  5. 注册全局中间件
  6. 注册路由
  7. 启动 HTTP 服务

目前的加载顺序大致是:

config -> service -> middleware -> global middleware -> routerSchema -> controller -> extend -> router

这个顺序本质上是依赖顺序:

  • config 是最基础的,很多模块初始化时会直接用 app.config
  • service 先于 controller,因为 controller 往往依赖 service
  • middleware 先加载定义,再在 app/middleware.js 中统一注册
  • routerSchema 要在请求真正进来前准备好,供参数校验中间件使用
  • controller 先于 router ,因为路由注册时要绑定 controller 方法
  • router 初始化的时候需要依赖 middleware, routerSchema, controller,因此放在最后

三、自动加载机制

Elpis 的一个核心特性是“自动加载”。

目前项目已经实现了这些 loader:

  • config loader
  • service loader
  • middleware loader
  • controller loader
  • router-schema loader
  • extend loader
  • router loader

它们的共同思路基本一致:

  • 使用 glob 扫描约定目录下的文件
  • 将文件名转换成对象属性名
  • 挂载到 app 对象上

使用时如下:

  • app.service.xxx
  • app.controller.xxx
  • app.middlewares.xxx
  • app.routerSchema.xxx

这让业务代码的组织方式更统一,也避免了每个模块里反复手动 require,用户只需要按照约定编写代码。

四、页面渲染能力

项目支持服务端模板渲染SSR - server side rendering,关键在于引入了 koa-nunjucks-2

在全局中间件中注册后,ctx 上就会拥有 render() 方法,代码如下:

// app/middleware.js
...
// 模版引擎渲染
const koaNunjucks = require('koa-nunjucks-2');
app.use(koaNunjucks({
    ext: 'tpl',
    path: path.resolve(process.cwd(), './app/public'),
    nunjucksConfig: {
        noCache: true,
        trimBlocks: true
    }
}));
...

// controller 中
await ctx.render('output/entry.xxx', data)

目前页面路由例如:/view/:page,访问后会根据动态参数渲染对应模板,比如:/view/page1

controller会进一步使用:ctx.params.page,拿到page1定位到对应模板app\public\output\entry.page1.tpl进行渲染。

五、API 接口处理能力

项目当前已经具备一条比较清晰的 API 请求处理链:

  1. 请求进入 Koa
  2. 经过全局中间件
  3. 执行签名校验
  4. 执行参数校验
  5. 进入 router.routes()
  6. 匹配到 controller
  7. 调用 service
  8. 返回响应

六、基于 JSON Schema 的接口参数校验

项目目前已经接入了 Ajv,通过 router-schema 目录维护接口的参数规则。

例如一个接口可以声明:

  • headers
  • query
  • body
  • params

对应的参数校验中间件会根据请求内容逐项校验。

七、签名校验机制

在参数校验之前,项目还做了 API 签名校验。

目前逻辑比较简单:

  • 从请求头中读取时间戳和签名
  • 使用约定的 signKey 生成签名
  • 判断签名是否一致
  • 判断请求是否超时

这部分实现虽然基础,但已经具备了接口防随意调用和基本防重放的雏形。

八、日志系统

extend/logger.js中实现,采用log4js 区分本地和线上环境,本地直接使用console,线上环境使用log4js把日志输出并落地到磁盘(日志落盘)

九、总结

从目前阶段来看,Elpis 已经不是一个简单的 Koa demo 了,而是一个具备框架能力的项目骨架。

完成的核心功能如下:

  • 自动加载机制 - 7个核心加载器
  • 分层架构设计 - Controller/Service
  • 环境管理系统 - 支持多环境部署
  • 配置管理系统 - 分环境配置覆盖
  • 扩展机制 - 灵活的框架扩展能力
  • 异常处理 - 全局错误捕获和处理
  • 参数验证 - 基于JSON Schema的API验证
  • 日志系统 - 分环境的日志处理

架构优势:

  • 约定优于配置,减少开发配置工作
  • 分层清晰,职责分离,易于维护
  • 自动化程度高,提升开发效率
  • 扩展性强,支持插件化开发
  • 企业级特性,支持多环境部署