一、项目描述
Elpis-Core 是一个基于 Koa 构建的轻量级 Web 框架,其设计理念类似于一个简化的 Egg.js。它采用模块化加载机制,通过"约定优于配置"的原则,实现了高度可扩展的应用架构。
其核心在于其"规范目录到自动加载"的逻辑,包含了多个独立的加载器,每个加载器对应一个核心功能模块(例如路由配置、控制器、中间件等)。这些加载器会自动扫描指定的目录,并将解析出的模块或功能挂载到 Koa 应用实例上,从而简化了开发者的配置工作,可以让开发者更加专注于业务之中。

二、目录结构
js
elpis/
├── elpis-core/ # 框架内核
│ ├── loader/ # 加载器系统
│ │ ├── config.js # 配置加载器
│ │ ├── controller.js # 控制器加载器
│ │ ├── service.js # 服务加载器
│ │ ├── middleware.js # 中间件加载器
│ │ ├── router.js # 路由加载器
│ │ ├── router-scheama.js# Schema加载器
│ │ └── extend.js # 扩展加载器
│ ├── env.js # 环境管理
│ └── index.js # 入口文件
│
├── app/ # 业务代码
│ ├── controller/ # 控制器层
│ ├── service/ # 服务层
│ ├── router/ # 路由定义
│ ├── router-scheama/ # 参数校验Schema
│ ├── middleware/ # 中间件
│ ├── extend/ # 扩展模块
│ ├── public/ # 静态资源
│ └── middleware.js # 全局中间件配置
│
├── config/ # 配置文件
│ ├── config.default.js # 默认配置
│ ├── config.local.js # 本地配置
│ ├── config.test.js # 测试配置
│ └── config.prod.js # 生产配置
│
└── index.js # 启动入口
三、核心设计理念
3.1 约定优于配置
通过统一的目录结构和命名规范,减少配置工作,提高开发效率;
约定规则: 
3.2 模块化加载机制
使用 glob 自动扫描目录,动态加载模块
加载器统一模式:
js
module.exports = (app) => {
// 1. 扫描目录
const dirPath = path.resolve(app.businessPath, `.${sep}target-dir`);
const fileList = glob.sync(path.resolve(dirPath, `.${sep}**${sep}**.js`));
// 2. 遍历文件
const modules = {};
fileList.forEach((file) => {
// 3. 提取模块名
let name = extractModuleName(file);
// 4. 命名转换(kebab-case → camelCase)
name = name.replace(/[-_]([a-z])/ig, (s) => s.substring(1).toUpperCase());
// 5. 加载模块
modules[name] = require(path.resolve(file))(app);
});
// 6. 挂载到 app
app.targetModule = modules;
}
- ✅ 无需手动导入
- ✅ 自动发现新模块
- ✅ 统一的加载逻辑
- ✅ 支持嵌套目录
3.3 依赖注入
设计思想 :通过 app 实例传递依赖,实现模块间的解耦。
依赖关系图 :
js
┌─────────┐
│ app │
└────┬────┘
│
┌────────────────┼────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ config │ │ service │ │ env │
└─────────┘ └────┬────┘ └─────────┘
│
┌─────▼─────┐
│controller │
└───────────┘
js
// BaseController
module.exports = (app) => {
return class BaseController {
constructor() {
this.app = app; // 注入 app 实例
this.service = app.service; // 注入 service
this.config = app.config; // 注入 config
}
}
}
3.4 环境管理
管理不同环境的配置和判断
js
module.exports = (app) => {
return {
getEnv() {
return process.env._ENV || 'local'
},
isLocalEnv() {
return process.env._ENV === 'local'
},
isTestEnv() {
return process.env._ENV === 'test'
},
isProdEnv() {
return process.env._ENV === 'prod'
}
}
}
------------------------------------------------------------
# 本地环境
npm run dev # set _ENV=local
# 测试环境
npm run test # set _ENV=test
# 生产环境
npm run prod # set _ENV=prod
3.5 配置加载
分层配置,环境配置覆盖默认配置
js
module.exports = (app) => {
// 1. 加载默认配置
const defaultConfig = require('./config.default.js');
// 2. 根据环境加载配置
let envConfig = {};
if (app.env.isLocalEnv()) {
envConfig = require('./config.local.js');
} else if (app.env.isTestEnv()) {
envConfig = require('./config.test.js');
} else if (app.env.isProdEnv()) {
envConfig = require('./config.prod.js');
}
// 3. 合并配置
app.config = Object.assign({}, defaultConfig, envConfig);
};
------------------------------------------------------------
环境配置 > 默认配置
3.5 中间件机制
中间件管道:
js
Request
↓
┌──────────────┐
│ errorHandler │ 错误处理
└──────┬───────┘
↓
┌──────────────┐
│ apiSignVerify│ 签名校验
└──────┬───────┘
↓
┌──────────────┐
│apiParamsVerify│ 参数校验
└──────┬───────┘
↓
┌──────────────┐
│ Router │ 路由处理
└──────┬───────┘
↓
Response
------------------------------------------------------------
module.exports = (app) => {
// 静态资源
app.use(koaStatic('./app/public'));
// 模板引擎
app.use(koaNunjucks({ ext: 'tpl' }));
// Body 解析
app.use(koaBody());
// 业务中间件
app.use(app.middlewares.errorHandler);
app.use(app.middlewares.apiSignVerify);
app.use(app.middlewares.apiParamsVerify);
};
3.6 MVC 分层架构
分层结构:
js
┌─────────────────────────────────────┐
│ Router (路由层) │ URL 映射
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ Controller (控制器层) │ 请求处理
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ Service (服务层) │ 业务逻辑
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ Model (数据层) │ 数据存储
└─────────────────────────────────────┘
------------------------------------------------------------
// Controller 示例:
// app/controller/project.js
module.exports = (app) => {
const BaseController = require('./base')(app);
const projectService = app.service.project;
return class ProjectController extends BaseController {
async getProjectList(ctx) {
const projectList = await projectService.getProjectList();
this.success(ctx, { data: projectList });
}
}
}
------------------------------------------------------------
// Service 示例:
// app/service/project.js
module.exports = (app) => {
const BaseService = require('./base')(app);
return class ProjectService extends BaseService {
async getProjectList() {
// 业务逻辑处理
return [{ id: 1, name: 'project1' }];
}
}
}
3.7 扩展机制
允许开发者扩展 app 对象,添加自定义功能
js
// app/extend/logger.js
module.exports = (app) => {
return {
info(msg) {
console.log(`[INFO] ${new Date().toISOString()} ${msg}`);
},
error(msg) {
console.error(`[ERROR] ${new Date().toISOString()} ${msg}`);
}
}
}
------------------------------------------------------------
// 在任何地方使用
app.logger.info('Application started');
app.logger.error('Something went wrong');
四、依赖关系分析
内核加载顺序
js
const start = (options) => {
app.options = options;
app.basePath = process.cwd();
app.businessPath = path.resolve(app.basePath, `.${sep}app`);
app.env = env(app);
// 1️⃣ 配置最先加载(其他模块依赖)
configLoader(app);
// 2️⃣ 基础模块加载
middlewareLoader(app);
routerSchemaLoader(app);
extendLoader(app);
// 3️⃣ 业务模块加载(依赖 config)
serviceLoader(app);
controllerLoader(app);
// 4️⃣ 全局中间件注册
require(`${app.businessPath}${sep}middleware.js`)(app);
// 5️⃣ 路由最后注册(依赖 controller)
routerLoader(app);
// 6️⃣ 启动服务
app.listen(port, host);
};
五、总结
- 开箱即用 - 内置完整的 MVC 架构
- 高度模块化 - 各模块职责清晰,易于维护
- 约定优于配置 - 减少配置工作,提高开发效率
- 可扩展性强 - 支持自定义扩展和中间件
- 参数校验完善 - 基于 JSON Schema 的参数验证
- 多环境支持 - 本地/测试/生产环境隔离
适用场景
- ✅ 中小型 Web 应用
- ✅ 服务端渲染(SSR)项目
- ✅ 微服务架构中的单个服务