Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

编写业务逻辑

在 Oak 中,Entity 只负责把“对象长什么样、能做什么动作”定义清楚;而一个真正可运行的业务系统,还必须再补上一层“对象如何联动、什么情况下允许操作、系统启动后要持续做什么”的运行时逻辑。

这些运行时逻辑,主要就写在 src 下面的这些目录中:

  • triggers
  • checkers
  • watchers
  • aspects
  • timers
  • routines
  • ports
  • features

它们虽然都属于“业务逻辑”,但解决的问题完全不同。

这一组概念分别是做什么的

trigger

trigger 负责数据联动。当某个对象发生 create/update/remove/select 等行为时,框架会在既定时机触发对应逻辑。它最适合表达“当 A 变化时,B 也必须同步变化”这一类约束。

checker

checker 负责合法性检查。它定义“某个动作在什么条件下允许发生”。和 trigger 最大的不同在于,checker 不只在后端执行,前端也可以利用它提前判断一个操作是否允许,从而获得前后端一致的行为。

常见的属性级更新限制,不一定要手写 checker;可以先看 src/configuration/attrUpdateMatrix.ts 是否能表达,框架会据此生成内置 checker。

watcher

watcher 负责轮询型后台任务。在 Oak 后端中,它会每 120 秒执行一轮,适合处理“不断检查数据库里有哪些待处理数据”的场景,例如重试失败消息、补偿异步状态、扫尾清理等。

aspect

aspect 可以理解为 Oak 中的命名业务服务。当一段逻辑不适合直接表达成某个单一实体上的 CRUD,或者需要把多次 select/operate 封装为一个明确的业务入口时,就适合写成 aspect

timer

timer按 cron 调度的任务。和 watcher 的固定 120 秒轮询不同,timer 的执行时机由 node-schedule 的 cron 表达式控制,适合明确的周期任务。

routine

routine应用启动或停止时执行一次的例程。它不解决周期问题,而是解决“应用刚启动时需要初始化什么、应用关闭前需要释放什么”。

port

port 是 Oak 对导入导出能力的抽象。它主要服务于 Excel 之类的结构化批量导入导出场景,让导入模板、解析逻辑、批量创建和批量导出都进入 Oak 的业务体系。

feature

feature 是前端侧的可复用状态与服务对象。它不属于后端一致性逻辑,而是 Oak 前端运行时的一部分,用来封装缓存、消息、导航、令牌、文件上传、业务工具类等横跨多个组件的能力。

它们之间最容易混淆的边界

Oak 新手最容易犯的错误,不是“不会写”,而是“写错地方”。

下面这几个判断非常重要:

  • 涉及数据一致性的约束,优先考虑 triggerchecker
  • 涉及显式业务服务入口,优先考虑 aspect
  • 涉及后台持续轮询,使用 watcher
  • 涉及 cron 调度,使用 timer
  • 涉及应用启动初始化,使用 routine
  • 涉及前端共享状态或工具封装,使用 feature

尤其不要把所有复杂逻辑都堆进 aspectaspect 很方便,但它本质上只是一个入口,不会自动替你解决对象间联动、权限推导、前后端一致检查这些更底层的问题。

一个建议的编写顺序

实际开发时,比较推荐的顺序通常是:

  1. 先定义 Entity
  2. 再用 checker 明确哪些操作允许发生;
  3. trigger 补齐对象联动;
  4. 如果需要后台扫描,再补 watchertimer
  5. 最后才去写 aspectfeatureport 这类更偏“入口”和“交互”的逻辑。

这样写出来的代码,会更贴近 Oak 本身的设计思路,也更容易维护。