EAMX 的插件化架构遵守两条设计原则。其一,中心只做注册和调度: PluginRegistry 不介入任何业务逻辑,其职责限于记录哪些插件完成了注册、汇总各插件声明的权限、登记各插件使用的路由前缀。 其二,一切通过契约交互:插件只需实现 PluginManifest 接口,无需感知其他插件的存在,也不需要知道自身在系统中的位置。
这一做法与微内核架构一致,其价值在于影响面。核心的每一次变更都会作用于全部业务模块,因此核心保留的范围越小,变更所波及的范围越小; 业务逻辑分散在各插件内部之后,插件之间的故障不会相互扩散。系统的稳定性来自核心的克制。
规模口径如下:PluginRegistry 74 行,LifecycleBus 79 行,两处合计 153 行核心实现, 支撑 Tier 1 至 Tier 3 共 11 个插件模块的注册、路由挂载、权限聚合与定时任务。
行数本身不构成结论。上述两处实现之所以精简,原因在于它们只保留了两件事:把模块登记进来,以及在确定的时机把事件分发出去。 状态机、流程定义、界面配置与数据模型均位于插件内部,不占用核心的行数。
每个插件在启动阶段提交一份 PluginManifest。契约字段按用途分为四组。
身份与版本:id 为该插件的唯一标识,例如 "equipment";name 为显示名称;
version 采用语义化版本;description 为可选说明。
依赖与分层:dependencies 声明该插件所依赖的其他插件,构成加载顺序约束——所列依赖必须已完成注册;
tier 声明该插件所处的层级,取值为 0 至 3。
生命周期钩子:lifecycleHandlers 为事件到处理器的映射,插件可在其中注册多个事件处理器。
资源注册:createRouter 返回一个 Express Router 实例;
routePrefix 声明该实例挂载的路由前缀,例如 "/api/equipment";
permissions 以 CASL 权限定义的形式声明该插件涉及的动作与对象;
navItems 提供前端导航项;cronJobs 声明定时任务。
| Tier | 定位 | 卸载与降级 |
|---|---|---|
| Tier 0 | 平台核心 | 不可卸载。平台认证与租户能力位于该层 |
| Tier 1 | 基础业务(EAM 基础功能) | 影响范围最大,后续层级依赖此层提供的模块 |
| Tier 2 | 业务增强(可选) | 影响范围限于该层所提供的增强功能 |
| Tier 3 | 实验与社区 | 可热拔插,加载失败不影响系统启动 |
Tier 决定的是卸载时的影响范围与降级策略,因此插件在提交契约时就已声明自身属于哪一层。 层级的作用是把模块之间的依赖方向固定下来,供启动阶段排序使用。
register() 在一个方法内完成三项检查与登记,全部发生在启动阶段。
id 重复注册时直接抛出错误,以避免同一模块被两次登记。dependencies 中的每个标识,确认其已完成注册;未完成时抛出错误,并指出缺失的依赖名称。lifecycleHandlers 中的每个事件与处理器注册到 LifecycleBus,插件无需在其他位置重复声明。三项检查的作用在于把配置错误留在启动阶段。在注册期失败,是一项可在测试环境解决的问题; 在运行期失败,则表现为某个业务动作在特定路径下失效,定位成本明显上升。
注册完成后,PluginRegistry 提供三处聚合能力。
mountAll(router) 遍历全部已注册插件,将 createRouter() 返回的 Router 按 routePrefix 挂载到父 Router;未声明 createRouter 的插件被跳过,其路由前缀同样不会出现在系统中。getAllPermissions() 将所有插件的权限定义合并为一个数组,交由权限层统一裁决。全系统使用同一套 CASL 规则,插件因此无需各自实现判定逻辑。getAllNavItems() 将所有插件的导航项合并,并按各导航项的 order 排序;未声明 order 的项按 99 参与排序,排在显式声明顺序的项之后。聚合结果与插件数量无关:核心只做合并与排序,不解析插件所声明的业务语义。
权限聚合决定的是工具清单的可见范围:动作契约共 247 条,覆盖 35 个业务域。权限判定在服务端完成, 工具清单由服务端按账号权限生成,客户端拿到的清单即为服务端的授权结果,前端不存在可绕过的调用路径。 演示账号为全部试用权限,其清单覆盖全部动作。上述范围可由演示环境当场核对总数与业务域覆盖,无需申请。查看核验方式 →
LifecycleBus 承担插件与业务动作之间的事件分发。两种模式的差别集中在执行顺序与失败的后果上。
emit 为观察型事件:全部处理器并行执行,结果由 Promise.allSettled 收集;
单个处理器失败只记录日志,不抛出异常,也不阻断主流程。该模式适用于通知、日志与缓存失效一类动作,
其处理结果不应改变业务动作是否成立。
intercept 为拦截型事件:处理器按注册顺序串行执行,可以在执行过程中修改 payload,也可以中断整个动作。 实现方式是把当前 payload 依次传入处理器,处理器通过 intercept 回调提交对 payload 的修改,修改结果作为下一个处理器的输入; 任一处理器调用 abort 时,总线抛出 AbortError,动作终止。
| 模式 | 用例 | 行为 |
|---|---|---|
| emit(观察) | 通知、日志、缓存失效 | 并行执行,失败仅记录日志,不阻断主流程 |
| intercept(拦截) | 审批流、数据校验、权限注入 | 串行执行,可修改 payload,可抛出 AbortError 中断 |
典型拦截场景:设备创建前,审批流插件检查该设备的审批策略;条件满足时流程继续,条件不满足时以 abort 中断,并给出中断原因。 该场景中,审批能力由插件提供,业务动作本身不需要知道审批规则的存在。
以审批流插件为例,可以看到一份完整契约的形态。其 id 为 approval,显示名称为审批流,版本为 1.0.0,tier 为 1,
dependencies 声明依赖 auth——该字段同时决定了 auth 必须先完成注册。
permissions 声明两条权限:对 WorkOrder 的 approve 动作,以及对 PurchaseRequest 的 approve 动作。
routePrefix 声明为 "/api/approval",createRouter 在该前缀下注册三个端点:
GET /pending 返回待办审批,POST /:id/approve 执行通过,POST /:id/reject 执行驳回。
lifecycleHandlers 在 equipment:beforeCreate 事件上注册处理器:设备价值超过 100000 元时调用 abort 中断创建流程,
中断原因为设备价值超过 10 万元、需要主管审批;设备价值未超过阈值时,处理器不作干预,创建流程按原路径继续。
该示例中,PluginRegistry 不需要知道审批流的存在。它接收的是一份符合契约的 manifest,注册流程与其余模块完全一致。 新增能力通过提交契约完成接入,核心代码不随之变更。
各层之间存在明确的依赖方向:Tier 1 的模块依赖 Tier 0 提供的平台能力,Tier 2 依赖 Tier 1 的模块,Tier 3 位于末端。
启动阶段按依赖关系完成拓扑排序,Tier 0 最先完成注册。任何前置模块未完成注册时,后续模块会在依赖检查处失败, 进程不会带着不完整的依赖关系继续启动。
Tier 1 至 Tier 3 的模块数如下:Tier 1 七个插件模块,Tier 2 三个,Tier 3 一个,合计 11 个。 Tier 0 的 auth 与 tenant 属平台核心,不参与模块计数。完整清单见下文「接口与实现片段」中的加载顺序片段。
Tier 3 的模块为实验性模块,以热拔插方式启用;加载失败时不阻断系统启动,影响范围限于该模块自身所提供的功能。
该内核提供的范围可归纳为五项:登记插件身份、汇总权限声明、挂载路由、生成导航、分发事件。 业务状态机、流程定义、界面配置与数据模型均位于插件内部,不进入核心。
注册阶段只执行幂等检查与依赖检查两项判定,核心不对插件声明的业务语义作进一步校验,契约字段的准确性由插件自身承担; 因此模块清单与权限清单的可信度,取决于插件实现与评审流程。
数字口径:Tier 1 至 Tier 3 共 11 个模块,Tier 0 不参与计数;核心实现的行数按文件计量,见 表 3。 本文只说明注册、加载与事件分发的实现方式;接入通道、权限裁决规则与工具清单分别由 AI 原生与 Agent 接入 与 CASL 同构权限系统 说明。
| 文件 | 行数 | 职责 |
|---|---|---|
packages/core/src/plugin-registry.ts | 74 | 插件注册、路由挂载、权限与导航聚合 |
packages/core/src/lifecycle-bus.ts | 79 | emit 与拦截两种模式的事件总线 |
packages/core/src/types.ts | — | PluginManifest 接口定义 |
plugin-registry.ts 74 行、lifecycle-bus.ts 79 行);模块清单(Tier 1 至 Tier 3 共 11 个)下列片段取自实现记录,为便于阅读省略了与本文无关的日志与异常处理。片段中的字段名、事件名与方法名即为该实现中的名称。
上述五个片段覆盖了本文所引用的全部实现细节:契约字段、注册流程、事件分发的两种模式、一份完整 manifest,以及各层的加载方向。 其余实现(业务状态机、流程定义、界面配置)位于各插件内部,不在核心范围内。
153 行核心实现、Tier 1 至 Tier 3 共 11 个模块、该账号可见工具数由端点实时返回、契约字段与事件名—— 上述各项均无需采信本文陈述,可由演示环境自行核实。