基于koa二次封装的服务端框架--elpis-core

注:引用抖音"哲玄前端",《全栈实践课》

前言

elpis-core 是一个基于 Koa.js 的应用框架,其核心目标是通过模块化、分层架构和设计模式的运用,为开发者提供高效、灵活且可维护的开发体验。它致力于解决复杂项目开发中的常见问题,如模块耦合、代码难以维护、功能扩展困难等。

项目整体架构设计

模块化设计思维

将应用划分为多个功能模块,如 config、extend、middleware、routerSchema、controller、service 等,每个模块有明确的职责和边界。这种设计便于团队协作,不同开发人员可以专注于各自擅长的模块,同时降低修改一个模块对其他模块的影响。

分层架构原则

遵循分层原则,将应用分为配置层、扩展层、中间件层、路由模式层、控制器层、服务层等。各层之间有清晰的接口定义,上层依赖下层,但下层不依赖上层,提高了系统的可维护性和可扩展性。

该框架使用了一系列的 loader来加载并挂载到 Koa 实例,每个 Loader 有明确的功能描述和关键技术点
  • configLoader(配置管理模块)
  • middlewareLoader(中间件加载器)
  • extendLoader(扩展加载)
  • routerSchemaLoader(接口参数校验)
  • serviceLoader(服务加载)
  • controllerLoader(控制器)
  • routerLoader(路由加载器)
config配置管理模块

通过不同环境配置文件(如 config.beta.jsconfig.prod.js),实现一键切换环境,满足开发、测试、生产等不同场景需求。config.default.js 作为基础配置,其他环境配置可在此基础上覆盖,平衡默认行为与灵活性。

  • configLoader 核心代码
js 复制代码
// 找到 config/ 目录
  const configPath = path.resolve(app.baseDir, `.${sep}config`);

  // 获取 default.config
  let defaultConfig = {};
  try {
    defaultConfig = require(path.resolve(configPath, `.${sep}config.default.js`));
  } catch (e) {
    console.log('[exception] there is no default.config file');
  }

  // 获取env.config
  let envConfig = {};
  try {
    if (app.env.isLocal()) { // 本地环境
      envConfig = require(path.resolve(configPath, `.${sep}config.local.js`));
    } else if (app.env.isBeta()) { // 测试环境
      envConfig = require(path.resolve(configPath, `.${sep}config.beta.js`));
    } else if (app.env.isProduction()) { // 生产环境
      envConfig = require(path.resolve(configPath, `.${sep}config.prod.js`));
    }
  } catch (e) {
    console.log('[exception] there is no default.config file');
  }
  // 覆盖并加载 config 配置
  app.config = Object.assign({}, defaultConfig, envConfig);
middleware中间件模块

灵活加载中间件并构建洋葱圈模型

  • middlewareLoader核心代码
js 复制代码
fileList.forEach(file => {
    // 提取文件名称
    let name = path.resolve(file);

    // 截取路径 app/middleware/custom-module/custom-middleware => custom-module/custom-middleware
    name = name.substring(name.lastIndexOf(`middleware${sep}`) + `middleware${sep}`.length, name.lastIndexOf('.'));

    // 把 '-' 统一改为驼峰式,custom-module/custom-middleware.js => customModule.customMiddleware
    name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());

    // 挂载 middleware 到内存 app 对象中
    let tempMiddleware = middlewares;
    const names = name.split(sep);
    for (let i = 0, len = names.length; i < len; ++i) {
      if (i === len - 1) {
        tempMiddleware[names[i]] = require(path.resolve(file))(app);
      } else {
        if (!tempMiddleware[names[i]]) {
          tempMiddleware[names[i]] = {};
        }
        tempMiddleware = tempMiddleware[names[i]];
      }
    }
  });
extend扩展功能

用于拓展应用的额外能力,例如日志

  • extendLoader核心代码
js 复制代码
// 遍历所有文件目录,把内容加载到 app.extend 下
  fileList.forEach(file => {
    // 提取文件名称
    let name = path.resolve(file);

    // 截取路径 app/extend/custom-extend => custom-extend
    name = name.substring(name.lastIndexOf(`extend${sep}`) + `extend${sep}`.length, name.lastIndexOf('.'));

    // 把 '-' 统一改为驼峰式,custom-extend.js => customExtend
    name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());

    // 过滤 app 已经存在的key
    for (const key in app) {
      if (key === name) {
        console.log(`[extend load error] name:${name} is already in app`);
        return;
      }
    }

    // 挂载 extend 到内存 app 对象中
    app[name] = require(path.resolve(file))(app);
  });
routerSchem路由模式定义

定义路由模式,便于生成标准路由配置,减少重复定义,同时为参数校验等提供依据

  • routerSchemaLoader核心代码
js 复制代码
// 注册所有 routerSchema ,使得可以 'app.routerSchema' 这样访问
  let routerSchema = {};
  fileList.forEach(file => {
    routerSchema = {
      ...routerSchema,
      ...require(path.resolve(file))
    }
  });
  app.routerSchema = routerSchema;
service服务加载器

提供业务逻辑,供 controller 调用

  • serviceLoader核心代码
js 复制代码
fileList.forEach(file => {
    // 提取文件名称
    let name = path.resolve(file);

    // 截取路径 app/service/custom-module/custom-service => custom-module/custom-service
    name = name.substring(name.lastIndexOf(`service${sep}`) + `service${sep}`.length, name.lastIndexOf('.'));

    // 把 '-' 统一改为驼峰式,custom-module/custom-service.js => customModule.customService
    name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());

    // 挂载 service 到内存 app 对象中
    let tempService = service;
    const names = name.split(sep); // [customModule(目录), customService(文件)]
    for (let i = 0, len = names.length; i < len; ++i) {
      if (i === len - 1) { // 文件
        const serviceModule = require(path.resolve(file))(app);
        tempService[names[i]] = new serviceModule();
      } else { // 文件夹
        if (!tempService[names[i]]) {
          tempService[names[i]] = {};
        }
        tempService = tempService[names[i]];
      }
    }
  });
  app.service = service;
controller控制器

控制器层,处理 HTTP 请求并调用服务层

  • controllerLoader核心代码
js 复制代码
// 遍历所有文件目录,把内容加载到 app.controller 下
  const controller = {};
  fileList.forEach(file => {
    // 提取文件名称
    let name = path.resolve(file);

    // 截取路径 app/controller/custom-module/custom-controller => custom-module/custom-controller
    name = name.substring(name.lastIndexOf(`controller${sep}`) + `controller${sep}`.length, name.lastIndexOf('.'));

    // 把 '-' 统一改为驼峰式,custom-module/custom-controller.js => customModule.customController
    name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());

    // 挂载 controller 到内存 app 对象中
    let tempController = controller;
    const names = name.split(sep); // [customModule(目录), customController(文件)]
    for (let i = 0, len = names.length; i < len; ++i) {
      if (i === len - 1) { // 文件
        const ControllerModule = require(path.resolve(file))(app);
        tempController[names[i]] = new ControllerModule();
      } else { // 文件夹
        if (!tempController[names[i]]) {
          tempController[names[i]] = {};
        }
        tempController = tempController[names[i]];
      }
    }
  });
  app.controller = controller;
router路由加载器

路由定义,映射对应的controller

  • routerLoader核心代码
js 复制代码
// 注册所有路由
  const fileList = glob.sync(path.resolve(routerPath, `.${sep}**${sep}**.js`));
  fileList.forEach(file => {
    require(path.resolve(file))(app, router);
  });
  // 路由注册到 app 上
  app.use(router.routes());
  app.use(router.allowedMethods());

总结

本项目通过 elpis-core 核心框架,实现了环境配置管理、路由模式定义、中间件链式调用等功能,为业务开发提供了坚实的基础。同时,项目结构清晰,符合大型项目的开发规范,具有良好的扩展性和可维护性。

相关推荐
子兮曰20 小时前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰20 小时前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万21 小时前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝21 小时前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋21 小时前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁1 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95271 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大1 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师1 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学1 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端