Files
pj0235-eai_agentplatform/docs/02_Architecture/AR10_应用可删除封装规范.md
eaiadmin 90031b75f3 docs: 重构仓库文档目录并迁移训练素材
按当前架构重组 docs 目录,统一中文命名与目录分层,并将训练原材料迁移到独立目录以保持架构文档边界清晰。
2026-09-22 23:23:16 +08:00

20 KiB
Raw Permalink Blame History

AR10 XApp Removable Packaging Specification

版本:V1.2 日期:2026-09-18
性质:规范性文件(normative)
适用对象:xapp,即一级业务包
关联文档:

  • AR05_工作台架构约定.md
  • AR09_对象命名规范.md
  • AR11_技能专员连接器可删除封装规范.md

1. 目标

本文定义 xapp 的封装方式,目标是让一个 xapp 在工程上达到如下删除标准:

  1. 删除一个后端目录
  2. 删除一个前端目录
  3. 删除一条注册项
  4. 不需要全仓手工搜改业务代码
  5. 重新构建后不出现编译错误
  6. 运行卸载脚本后,不残留该 xapp 自有表、路由、菜单、任务、通知、前端状态

本文中的“可删除”指:

  • 删一个目录 + 删一条注册项 + 跑一次卸载 = 安全移除一个 xapp

严格意义上,“删一个文件就彻底删除”只适用于插件二进制或代码生成包;在当前单仓工程内,合理目标应为“删一个目录”。

2. 范围与非目标

2.1 本文覆盖

本文覆盖 xapp 的以下封装维度:

  1. manifest
  2. registry
  3. 前后端路由注册
  4. provider 暴露
  5. 数据边界
  6. schema 与 seed
  7. 卸载协议
  8. 定义条目所有权
  9. 共享引用降级协议
  10. 守卫与验收标准

2.2 本文不覆盖

本文不讨论以下内容的具体业务实现:

  1. 某个 xapp 内部页面应该长什么样
  2. 某个 xapp 的领域模型如何详细设计
  3. 某个 xapp 的 UI 风格如何命名

这些属于对象内部实现,不属于封装边界规范本身。

3. 当前问题

当前仓库已经存在 xapp_definition、前端 /xapps/... 路由以及若干 XAppShell,但多数业务 xapp 仍停留在“入口像 xapp,底层仍是平台散装模块”的阶段。典型表现如下:

  1. 平台总路由中仍手写某个 xapp 的业务路由
  2. 平台总导航中仍手写某个 xapp 的菜单
  3. 平台层 API 直接查询某个 xapp 的表
  4. xapp 的核心对象仍放在通用 model 包,而非 xapp 自己的域包
  5. 持久化模型直接兼任 API DTO 和领域对象
  6. 统计、画像、后台管理直接耦合业务表,而不是通过 provider 聚合
  7. xapp 的任务、通知、缓存、前端状态未形成 own 命名空间
  8. xapp_definition、目录条目、seed 定义记录等定义层资源尚未进入卸载协议
  9. 历史任务、收藏、通知历史等共享引用在对象删除后的处理策略未定义

这意味着当前不少 xapp 仍停留在“挂了 xapp 皮肤的并行模块”阶段,尚未达到可插拔业务包标准。

4. 定义

4.1 什么是 xapp

在本项目中,xapp 的定义是:

  1. 一个可注册的一级业务包
  2. 一个有 manifest 的对象
  3. 一个有自有 schema 的领域边界
  4. 一个只通过 contract 暴露能力的模块
  5. 一个可以通过“删目录 + 删注册 + 跑卸载”安全移除的工程单元

4.2 什么不是 xapp

以下对象不应按 xapp 方式建模:

  1. 单个技能
  2. 单个专员
  3. 单个连接器
  4. 一张单页配置页
  5. 一个普通 store 或 API 文件

这些对象的封装规则见 AR11。

5. 封装目标

每个 xapp 必须完整拥有以下六类内容:

  1. Manifest
  2. Frontend shell
  3. Backend module
  4. Domain model / repo / service
  5. Owned schema
  6. Integration adapters

平台层只负责:

  1. 注册 xapp
  2. 聚合 xapp 暴露的能力
  3. 为 xapp 提供公共底座能力

平台层不负责:

  1. 直接进入 xapp 内部目录取对象
  2. 直接查询 xapp 自有表
  3. 直接 hardcode 某个 xapp 的页面、菜单、统计、通知
  4. 直接依赖 xapp 内部 repo / service / views

6. 依赖方向

xapp 封装能否成立,核心不在于目录是否漂亮,而在于依赖方向是否单一。

正确依赖方向应为:

platform core
  -> xapps/core/contracts
  -> xapps/core/registry
  -> xapp manifest
  -> xapp module install / register

xapp internal
  -> xapp domain / repo / service / dto / schema

错误依赖方向包括:

  1. platform api -> xapp repo
  2. platform stats -> xapp table
  3. platform nav -> xapp views path literal
  4. platform seed -> xapp domain object

一句话:平台层可以认识“这个 xapp 存在”,但不能认识“这个 xapp 里面具体有什么文件”。

7. 推荐目录结构

7.1 后端

backend-go/internal/xapps/
  core/
    contracts.go
    manifest.go
    registry.go
    uninstall.go
  apps/
    internal_exam/
      manifest.go
      module.go
      api/
        student.go
        admin.go
      domain/
        paper.go
        record.go
        mistake.go
      repo/
        paper_repo.go
        record_repo.go
        mistake_repo.go
      service/
        exam_service.go
        stats_service.go
      dto/
        request.go
        response.go
      schema/
        migrations.go
        uninstall.go
      assets/
      tests/
    internal_training/
      manifest.go
      module.go
      api/
      domain/
      repo/
      service/
      dto/
      schema/
      assets/
      tests/

7.2 前端

frontend/src/xapps/
  core/
    contracts.js
    registry.js
    installer.js
    uninstall.js
  apps/
    internal-exam/
      manifest.js
      routes.js
      nav.js
      providers.js
      api/
      store/
      views/
      components/
      assets/
    internal-training/
      manifest.js
      routes.js
      nav.js
      providers.js
      api/
      store/
      views/
      components/
      assets/

8. 核心硬规则

8.0 一对象一目录是最终形态

对 xapp 而言,最终封装形态必须满足:

  1. 一个 xapp 对应一个后端目录
  2. 一个 xapp 对应一个前端目录
  3. 该 xapp 的业务定义、路由定义、provider 定义、schema 定义都落在自己的目录中

因此:

  1. 允许中心化注册
  2. 不允许中心化定义

这里的“中心化注册”指:

  1. registry 统一列出有哪些 xapp manifest
  2. installer 统一执行 install / uninstall
  3. platform core 统一聚合 provider

这里的“中心化定义”指:

  1. 在一个平台公共文件里手写多个 xapp 的业务路由
  2. 在一个平台公共文件里手写多个 xapp 的菜单项
  3. 在一个平台公共文件里手写多个 xapp 的 schema / provider / page path

前者是允许的,后者不是最终封装形态。

8.0.1 允许中心化 / 禁止中心化

类型 是否允许 说明
xapps/core/registry 集中列出 manifest 允许 该文件承担注册中心职责
xapps/core/installer 统一安装路由和导航 允许 这是装配层
平台总路由手写 internal-exam 业务路径 禁止 这是业务定义泄漏
平台总导航手写 internal-training 菜单细节 禁止 这是业务定义泄漏
平台公共文件集中维护多个 xapp 的 page path / provider key / table 名 禁止 这会破坏目录级删除

8.1 平台层不能直接 import xapp 内部实现

平台层只能依赖:

  1. xapps/core/registry
  2. manifest
  3. contracts/interface

平台层不能依赖:

  1. internal_exam/domain/*
  2. internal_exam/repo/*
  3. internal_exam/service/*
  4. internal_exam/views/*
  5. 任何其他 xapp 内部文件

这条规则的目的,是保证删除某个 xapp 目录后,不会炸掉全局依赖图。

8.2 路由必须由 xapp 自己注册

总路由不得手写某个 xapp 的业务路径,例如:

  1. /api/exam/*
  2. /xapps/internal-exam/*
  3. /courses/*
  4. /products/*

正确做法:

  1. 平台加载 xapp registry
  2. xapp 在 module.go / manifest.js 中自注册前后端路由

后端示意:

xappRegistry.Register(internalexam.Manifest)

前端示意:

xappRegistry.register(internalExamManifest)

8.3 数据表必须归属到 xapp 命名空间

为了实现“删目录即可删表”,表名必须体现归属。

不推荐:

  1. exam_paper
  2. exam_record
  3. mistake_record
  4. learning_progress

推荐:

  1. xapp_exam_paper
  2. xapp_exam_record
  3. xapp_exam_mistake
  4. xapp_training_progress

如果某张表是平台共享能力,而不是某个 xapp 私有数据,则应放入 shared 域,不允许伪装成 xapp 私有对象。

8.4 核心对象不能继续停留在通用 model 总包

xapp 私有业务对象必须放入自己的域目录,例如:

internal/xapps/apps/internal_exam/domain/

平台 internal/model 中只保留:

  1. 平台公共对象
  2. 跨 xapp 共享对象
  3. 公共底座对象

8.5 持久化模型不能直接充当 API DTO

必须分层:

  1. domain:领域对象
  2. repo entity:持久化对象
  3. dto request/response:接口契约

禁止继续使用如下模式:

  1. ShouldBindJSON(&model.ExamPaper{})
  2. API 直接返回 ORM entity
  3. 统计逻辑直接依赖表字段细节

8.6 统计、画像、后台管理只能通过 provider 聚合

平台层不得直接查询某个 xapp 的私有表来构建:

  1. 首页统计
  2. 学员画像
  3. 部门统计
  4. 后台管理报表

应改为由 xapp 对外暴露 provider:

type XAppStatsProvider interface {
    BuildAdminStats(ctx context.Context) (any, error)
}

type XAppProfileProvider interface {
    BuildUserProfile(ctx context.Context, userID uint) (any, error)
}

平台只聚合 provider 输出,不碰 xapp 内部表结构。

8.7 菜单、通知、任务、前端状态必须归 xapp 自己管理

不得把以下内容散落在平台全局文件中:

  1. 导航项
  2. 页面路由 path 判断
  3. 通知跳转链接
  4. 定时任务
  5. seed 数据入口
  6. localStorage key
  7. 前端 layout 的特殊 case

这些都必须由 xapp manifest 暴露。

8.8 定义条目也属于 xapp 的 own boundary

xapp 的封装边界不仅包括代码、路由、表和缓存,也包括“定义层资源”。

定义层资源至少包括:

  1. xapp_definition 中属于该 xapp 的定义记录
  2. 对象目录、市场目录、对象中心中的条目
  3. seed 自动生成的对象定义记录
  4. 前端目录页、治理页中依赖定义中心生成的条目

如果一个 xapp 删除后,这些定义条目仍然残留,那么:

  1. 用户仍可能看到僵尸入口
  2. 后台仍可能显示无效定义
  3. 平台仍可能尝试按已删除对象做跳转或查询

因此,定义条目必须被视为 owned resource,纳入 manifest 和卸载协议。

9. Manifest 与 Registry 设计

9.1 后端 manifest

type XAppManifest interface {
    Key() string
    Meta() XAppMeta
    RegisterRoutes(r gin.IRouter)
    RegisterProviders(reg ProviderRegistry)
    RegisterMigrations(reg MigrationRegistry)
    RegisterSeeds(reg SeedRegistry)
    RegisterJobs(reg JobRegistry)
    RegisterNotifications(reg NotificationRegistry)
    OwnedTables() []string
    OwnedStorageKeys() []string
    OwnedJobKeys() []string
    OwnedNotificationKeys() []string
    OwnedDefinitionKeys() []string
    OwnedCatalogEntries() []string
    SharedReferencePolicy() SharedReferencePolicy
}

后端 manifest 至少负责:

  1. 路由注册
  2. provider 注册
  3. migration 注册
  4. seed 注册
  5. job 注册
  6. 通知注册
  7. 定义条目归属声明
  8. 共享引用处理策略声明

9.2 前端 manifest

export default {
  key: 'internal-exam',
  label: '内部考试APP',
  baseRoute: '/xapps/internal-exam',
  install({ navRegistry, routeRegistry, providerRegistry, storeRegistry }) {},
  uninstallMeta: {
    ownedStorageKeys: ['xapp:internal-exam:*'],
    ownedRouteNames: ['InternalExam*'],
    ownedNavKeys: ['xapp.internal-exam.*'],
    ownedDefinitionKeys: ['xapp.internal-exam.definition'],
    ownedCatalogEntries: ['catalog.xapp.internal-exam'],
  },
}

前端 manifest 至少负责:

  1. 页面路由注册
  2. 菜单注册
  3. store 注册
  4. provider 注册
  5. 前端状态资源声明
  6. 前端定义条目资源声明

9.3 Registry 的职责

registry 只做两件事:

  1. 收集 manifest
  2. 统一安装 / 卸载 / 枚举

registry 不应承担:

  1. 业务逻辑
  2. repo 查询
  3. 页面渲染
  4. 领域对象转换

10. 卸载协议

“可删除”不是只删代码,还必须定义资源清理协议。

10.1 卸载输入

卸载协议至少应支持以下输入:

  1. xapp key
  2. dry_run
  3. drop_tables
  4. clear_storage
  5. clear_jobs
  6. clear_notifications

10.2 卸载输出

卸载结果至少应返回:

  1. 实际清理的表
  2. 实际清理的 job key
  3. 实际清理的 notification key
  4. 实际清理的 storage key
  5. 未清理成功的残留项
  6. 实际清理的定义条目
  7. 被共享引用阻塞或降级处理的项

10.3 卸载原则

卸载必须遵守以下原则:

  1. 只清理 manifest 声明过的 owned 资源
  2. 默认支持 dry_run
  3. 禁止越权删除共享资源
  4. 共享表必须由 shared 域自己维护,不得被 xapp 卸载误删

10.4 定义条目清理协议

卸载 xapp 时,必须同时清理或冻结以下定义层资源:

  1. xapp_definition 中的定义记录
  2. 对象目录或市场目录中的条目
  3. seed 自动生成的默认定义条目

允许的处理方式包括:

  1. 直接删除
  2. 标记为 tombstone
  3. 标记为 disabled 且不再对用户可见

但无论采用哪种方式,都必须保证:

  1. 平台不会再把它当成可安装、可进入、可查询的有效 xapp
  2. 前后端目录与导航中不再出现死入口

10.5 Shared Reference Policy

xapp 删除时,除 own resource 外,还必须处理共享域对它的历史引用。

典型共享引用包括:

  1. 历史任务记录
  2. 项目绑定关系
  3. 收藏 / 最近使用
  4. 通知历史
  5. 审计日志
  6. 历史发布版本

规范允许以下四类处理策略:

  1. block_uninstall:仍存在强引用时禁止卸载
  2. convert_to_tombstone:转为“对象已移除”墓碑态
  3. detach_reference:解除引用但保留历史记录
  4. readonly_history:保留只读历史,不允许继续进入对象

xapp manifest 必须声明自己的共享引用处理策略,平台卸载器必须在 dry_run 结果中明确报告:

  1. 哪些引用会被阻塞
  2. 哪些引用会被降级
  3. 哪些引用会被直接解除

10.6 Dry-run Gate 与执行守卫

卸载协议不仅要能执行,还必须能被平台守卫。

至少应具备以下守卫:

  1. uninstall dry_run 报告
  2. owned resource audit
  3. 定义条目完整性检查
  4. 共享引用阻塞检查
  5. CI 中的封装边界检查

11. Owned 资源矩阵

一个 xapp 的 own boundary 至少应覆盖以下资源:

资源类型 是否必须声明 典型例子
后端目录 必须 backend-go/internal/xapps/apps/internal_exam/
前端目录 必须 frontend/src/xapps/apps/internal-exam/
后端路由 必须 /api/xapps/internal-exam/*
前端路由 必须 /xapps/internal-exam/*
owned tables 必须 xapp_exam_record
owned storage keys 必须 xapp:internal-exam:*
definition keys 必须 xapp.internal-exam.definition
catalog entries 必须 catalog.xapp.internal-exam
provider keys 必须 xapp.internal-exam.stats
job keys 视需要 xapp.internal-exam.daily-sync
notification keys 视需要 xapp.internal-exam.record-published
nav keys 必须 xapp.internal-exam.entry

12. 当前培训 / 考试域的拆分建议

12.1 应进入 internal_exam xapp 的内容

应归入:

  1. 考试配置
  2. 考试记录
  3. 错题本
  4. 发证逻辑
  5. 学员考试流程
  6. 考试统计 provider
  7. 考试后台管理接口
  8. 考试前端 views / api / store

当前对象对应:

  1. ExamPaper
  2. ExamRecord
  3. MistakeRecord

12.2 应进入 internal_training xapp 的内容

应归入:

  1. 课程
  2. 产品知识
  3. 公司介绍
  4. 培训入口
  5. 培训前端 views / api / store
  6. 培训进度统计 provider

12.3 LearningProgress 的边界

LearningProgress 当前记录的是:

  1. company
  2. product
  3. course

因此它不应进入 internal_exam。

它有两种合理归属:

  1. 归入 internal_training
  2. 归入 shared learning 域

选择标准如下:

  1. 如果未来只有培训 xapp 使用,则归 internal_training
  2. 如果未来知识库、认证、课程中心等多个 xapp 都会消费,则归 shared learning

13. 迁移顺序

推荐按以下顺序实施,避免边搬边炸:

  1. 先建立 xapps/core/contracts
  2. 建立 registry
  3. 让 exam / training 先改为 manifest 注册
  4. 将平台直查表的逻辑改为 provider 调用
  5. 再搬迁 domain / repo / service
  6. 再把 DTO 与持久化对象分层
  7. 最后再改表名命名空间
  8. 最后补卸载脚本

这个顺序的核心原因是:

  1. 先修正依赖方向
  2. 再修正代码落点
  3. 再修正对象边界
  4. 最后修正资源所有权

14. 判定红线

如果一个 xapp 还存在以下任意一条,则不算完成封装:

  1. 总路由中仍手写它的业务路径
  2. 总导航中仍手写它的业务菜单
  3. 平台 api/*.go 仍直接查询它的私有表
  4. 平台 model 总包仍承载它的核心业务对象
  5. 别的模块还能直接 import 它的内部包
  6. 数据表名未进入它 own 的命名空间
  7. 统计、画像、后台管理未通过 provider 输出
  8. 前端主 layout 仍有针对它的硬编码 path 判断
  9. 卸载协议无法枚举它的 owned 资源
  10. 删除目录后仍需手工搜索多处散装注册点
  11. 一个公共定义文件里仍集中维护多个 xapp 的业务路由、菜单、provider、schema 或 path
  12. xapp_definition、目录条目或 seed 定义记录未进入 own boundary
  13. 历史任务、收藏、通知历史等共享引用没有明确降级策略
  14. 缺少 dry_run、边界检查或资源审计守卫

14.1 迁移期兼容层的限制

本规范允许迁移期存在少量兼容层,但必须满足以下条件:

  1. 兼容层只能做转发、注册、兼容映射
  2. 兼容层不得继续承载多个 xapp 的业务定义
  3. 兼容层必须有明确退场目标,不能成为长期正式结构

换句话说:

  1. registry 可以中心化
  2. compat adapter 可以暂存
  3. definition hub 不允许长期存在

14.2 Enforcement / Guard

为了防止封装边界回退,平台至少应建立以下自动守卫:

  1. import boundary lint:禁止平台层直接 import xapp 内部实现
  2. registry completeness check:已注册对象必须能完整枚举 own 资源
  3. owned resource audit:检查 manifest 声明和实际资源是否一致
  4. uninstall dry-run gate:卸载前必须可生成风险报告
  5. CI fail 条件:若出现未声明资源、未处理共享引用或越权依赖,则直接失败

15. 验收清单

一个 xapp 只有满足以下条件,才算达到“整包可删除”:

  1. 删除 backend-go/internal/xapps/apps/<xapp>/
  2. 删除 frontend/src/xapps/apps/<xapp>/
  3. 删除 registry 里的注册项
  4. 后端构建通过
  5. 前端构建通过
  6. 没有平台层 import 残留
  7. 没有平台级 hardcoded path 残留
  8. 运行卸载脚本后,该 xapp 的 owned tables 被清理
  9. 前端导航、页面、缓存、localStorage 全部消失
  10. 平台统计只少这一个 xapp 的 provider 结果,不出现空指针或编译错误
  11. job / notification / provider key 不残留
  12. 共享域不被误删
  13. xapp_definition、目录条目、seed 定义条目不会残留死入口
  14. dry_run 报告能正确列出 shared reference 的阻塞与降级结果

16. 最终标准

xapp 的最终定义应为:

  1. 一个可注册的业务包
  2. 一个有 manifest 的对象
  3. 一个有自有 schema 的领域边界
  4. 一个只通过 contract 暴露能力的模块
  5. 一个可以通过“删目录 + 删注册 + 跑卸载”安全移除的工程单元

只有达到这个标准,xapp 才能成为真正的一级对象。