编写业务逻辑
在 Oak 中,Entity 只负责把“对象长什么样、能做什么动作”定义清楚;而一个真正可运行的业务系统,还必须再补上一层“对象如何联动、什么情况下允许操作、系统启动后要持续做什么”的运行时逻辑。
这些运行时逻辑,主要就写在 src 下面的这些目录中:
triggerscheckerswatchersaspectstimersroutinesportsfeatures
它们虽然都属于“业务逻辑”,但解决的问题完全不同。
这一组概念分别是做什么的
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 新手最容易犯的错误,不是“不会写”,而是“写错地方”。
下面这几个判断非常重要:
- 涉及数据一致性的约束,优先考虑
trigger或checker; - 涉及显式业务服务入口,优先考虑
aspect; - 涉及后台持续轮询,使用
watcher; - 涉及 cron 调度,使用
timer; - 涉及应用启动初始化,使用
routine; - 涉及前端共享状态或工具封装,使用
feature。
尤其不要把所有复杂逻辑都堆进 aspect。aspect 很方便,但它本质上只是一个入口,不会自动替你解决对象间联动、权限推导、前后端一致检查这些更底层的问题。
一个建议的编写顺序
实际开发时,比较推荐的顺序通常是:
- 先定义
Entity; - 再用
checker明确哪些操作允许发生; - 用
trigger补齐对象联动; - 如果需要后台扫描,再补
watcher或timer; - 最后才去写
aspect、feature、port这类更偏“入口”和“交互”的逻辑。
这样写出来的代码,会更贴近 Oak 本身的设计思路,也更容易维护。