Elpis-Core — 融合 Koa 洋葱圈模型实现服务端引擎

一、项目描述

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);
};

五、总结

  1. 开箱即用 - 内置完整的 MVC 架构
  2. 高度模块化 - 各模块职责清晰,易于维护
  3. 约定优于配置 - 减少配置工作,提高开发效率
  4. 可扩展性强 - 支持自定义扩展和中间件
  5. 参数校验完善 - 基于 JSON Schema 的参数验证
  6. 多环境支持 - 本地/测试/生产环境隔离
适用场景
  • ✅ 中小型 Web 应用
  • ✅ 服务端渲染(SSR)项目
  • ✅ 微服务架构中的单个服务
相关推荐
codetown1 小时前
2026年Zig编程语言权威指南:从系统级底层架构到现代软件工程实践
后端·程序员
Java小卷2 小时前
流程设计器为啥选择diagram-js
前端·低代码·工作流引擎
HelloReader3 小时前
Isolation Pattern(隔离模式)在前端与 Core 之间加一道“加密网关”,拦截与校验所有 IPC
前端
兆子龙3 小时前
从 float 到 Flex/Grid:CSS 左右布局简史与「刁钻」布局怎么搞
前端·架构
cg333 小时前
cc-connect,十分钟帮你把 claude code 连接到微信,飞书,钉钉等等平台
后端·openai
YukiMori233 小时前
一个有趣的原型继承实验:为什么“男人也会生孩子”?从对象赋值到构造函数继承的完整推演
前端·javascript
用户1427868669323 小时前
Java多态的底层真相:JVM到底怎么知道该调哪个方法?(面试高频)
后端
_哆啦A梦3 小时前
Vibe Coding 全栈专业名词清单|设计模式·基础篇(创建型+结构型核心名词)
前端·设计模式·vibecoding
百里静修3 小时前
一个 Hook 拦截所有 AJAX 请求:ajax-hooker 使用指南与原理
前端