里程碑一 - elpis-code 内核引擎

139 阅读4分钟

抖音“哲玄前端”《大前端全栈实践》里程碑一的回顾与思考

BFF 层架构设计

elpis-code 内核引擎设计

通过解析器 elpis-core 解析 app 文件夹下的文件,并挂载到 koa 实例上。利用 koa 洋葱圈模型实现中间件处理机制,启动运行时服务 - 简化版 egg.js

  • config loader 加载环境配置文件。提供默认配置,并提供对开发环境,测试环境,生产环境不同环境进行差异化配置的能力

  • controller loader 加载 controller 模块,提供核心业务处理能力

    • BaseController 基类收拢 controller 相关公共方法。比如

      • API 处理成功时统一返回结构
      • API 处理失败时统一返回结构
    • ProjectController - API 响应

    • ViewController - 页面渲染,服务端渲染

  • extend loader 加载 extend 模块。扩展能力,挂载在全局 app 实例上

    • 日志 - log4js
  • middleware loader 加载中间件模块,挂载到 app 实例上

    • api 参数校验
    • api 签名合法性校验
    • 运行时异常错误处理,兜底所有异常
    • ...
  • global-middlerware 注册全局中间件,洋葱圈外层

  • router loader 加载路由模块。路由统一管理 - KoaRouter

  • router-schema loader 加载 json-schema 配置通过 'json-schema & ajv' 对 API 规则进行约束, 配合 api-parms-verify 中间件使用

  • service loader 加载 service 模块,服务层处理

    • 读写 mysql
    • 日志
    • ...

loader 加载顺序的考量

暂时无法在飞书文档外展示此内容

  1. 配置优先
  2. 扩展能力提前
  3. middleware 挂载到 app 实例上,等待注册使用
  4. global-middleware 注册全局中间件(洋葱圈外层)
  5. 进入服务层 - service(被 controller 依赖)
  6. router-schema 的 json 一般配置在 controller 和路由之前
  7. controller 做核心业务处理,连接服务层和展示层 - 洋葱圈内层
  8. router 依赖于 controller - 洋葱圈内层

依据

  • 约定优于配置 - 存在一定沟通成本
  • 执行顺序
  • 依赖关系

问题

  1. 全局中间件无法获取路由参数 params

    1. 原因:全局中间件的执行顺序早于路由中间件,而路由参数 params 由 koa-router 在路由级中间件注入

    2. 解决方案

      • API 参数校验中间件不注册为全局中间件使用,通过 router.get(path, middleware, handler) 注入到具体路由
      • 调整中间件执行顺序,破坏结构,不符合设计逻辑。如果路由不匹配,后续中间件不会执行
      • 全局手动解析 url 路径获取参数,灵活性较差
    3. 选择精准注入具体路由方式进行 API 校验,需要校验的路由注入,不需要校验的则不注入。灵活可控

  2. 服务端渲染 - ssr

    koa-router 调用 ctx.render(),引擎将模板 + 数据合并为 HTML

    1. koa-static 配置静态根目录
    2. koa-nunjucks-2 模板渲染引擎。向 ctx 注入 render 方法,支持服务端拼接 HTML
    3. koa-bodyparser 处理表单提交,结合模板重新渲染页面
  3. 302 临时重定向

    1. 为什么不使用 301

      • 兜底路由用于处理无效路径,重定向到指定路径
      • 301 会缓存重定向的结果,后续所有请求直接跳到旧路径
      • 使用 302 重定向,浏览器不会将首页与无效路径关联
    2. 301 302 303 的区别

      • ​​状态码​​​​语义​​​​浏览器行为​​​​SEO影响​​​​典型场景​​
        ​​301​​永久重定向缓存重定向结果,后续请求直接跳新URL搜索引擎转移权重至新URL,旧URL被淘汰域名永久更换、目录结构调整
        ​​302​​临时重定向​​不缓存​​重定向结果,每次请求均需验证跳转搜索引擎保留旧URL索引,新URL不继承权重A/B测试、临时维护页
        ​​303​​见其他资源强制使用GET方法重定向(防表单重复提交)同302,不传递权重POST请求后需跳转到结果页的场景

项目结构

elpis
├─ .eslintignore
├─ .eslintrc
├─ app
│  ├─ controller
│  │  ├─ base.js
│  │  ├─ middleware
│  │  ├─ project.js
│  │  └─ view.js
│  ├─ extend
│  │  └─ logger.js
│  ├─ middleware
│  │  ├─ api-params-verify.js
│  │  ├─ api-sign-verify.js
│  │  └─ error-handler.js
│  ├─ middleware.js
│  ├─ router
│  │  ├─ project.js
│  │  └─ view.js
│  ├─ router-schema
│  │  └─ project.js
│  └─ service
│     ├─ base.js
│     └─ project.js
├─ config
│  ├─ config.beta.js
│  ├─ config.default.js
│  └─ config.prod.js
├─ elpis-core
│  ├─ env.js
│  ├─ index.js
│  └─ loader
│     ├─ config.js
│     ├─ controller.js
│     ├─ extend.js
│     ├─ middleware.js
│     ├─ router-schema.js
│     ├─ router.js
│     └─ service.js
├─ index.js
├─ package.json
└─ README.md

总结

约定优于配置,分层明确,扩展能力强