用途:为 AdonisJS 项目提供 Action + 事件模式的实现规范,把业务逻辑与副作用解耦。控制器保持轻薄,按“策略 → 校验 → Action → 响应”流程调用 Action。
主要能力与规则:Action 位于 app/<mod>/actions/<verb_noun>.ts,每个文件一个 Action,默认导出含单个公开方法 async handle(input): Promise<Result | void> 的类;input 为普通接口,不使用 HttpContext。正常路径返回主值(模型、id 或 void),预期失败路径从 app/<mod>/exceptions/ 抛出领域异常。
副作用一律通过事件发出,不内联调用,包括邮件、应用内通知、SSE/实时广播、外部 HTTP 以及跨切面的审计日志写入。监听器放在 app/<mod>/start/events.ts,用 emitter.on('event:name', async (data) => { ... }) 注册,并在 adonisrc.ts 中预加载该文件。事件命名采用 <mod>:<过去式动词>,如 user:registered、invoice:issued,载荷为可序列化的普通数据。
并发处理:对“先读后写”的派生值(列表下一位置、序列下一编号、唯一 slug),需在 schema 上加 unique(...) 作为兜底,并用 db.transaction() 配合 SELECT ... FOR UPDATE 锁定父行来串行化计算与插入;删除并重排同理。重 I/O(文件上传、外部 HTTP)应放在事务外,避免长时间持锁。
测试:用 sinon 桩对 Action 做单元测试;端点测试可用 emitter.fake(['user:registered']) 与 fake.assertEmitted('user:registered') 验证事件发出;监听器可端到端测试,直接 emit 后断言副作用。
限制与反模式:Action 不得接收 HttpContext、不得直接调用 mail.send、facteur.notification(...).send()、transmit.broadcast 等;不得在 Action 内实例化另一个 Action,共享逻辑应抽为 service 或 query;不得在 db.transaction(...) 内 emit,应在事务提交后发出;监听器不应直接访问其他模块的数据库。
文档参考:AdonisJS emitter — https://docs.adonisjs.com/guides/digging-deeper/emitter。具体安装命令与版本要求暂未提供。