Skip to content

产品装配与远程 App 开发规范 ​

适用于 apps/* 的远程制品、所有 products/* 的装配清单、产品运行时、能力 Adapter、业务插件,以及组件服务平台。

规范目标 ​

通过“成熟能力复用 + 声明式装配 + 插件代码编排”快速构建软件。配置不得替代复杂业务代码,远程化不得破坏 NEXA 分层、类型安全和产品自治。

架构决策见 ADR-001。

资产模型 ​

text
Component Artifact
  └── Component Manifest
          ↓ 被 Product Manifest 锁定
Product Manifest
          ↓ 被 Product Runtime 解释
Product Shell + Plugin Runtime + Capability Adapters

装配平台只导出一个 product.composition.json。它同时记录产品与 Host Contract 精确版本、Shell 配置、固定的工作区插槽、Renderer、Container、能力绑定、不可变制品地址和 SRI,是发布、回滚与运行时加载的唯一装配真相源。

Shell 配置覆盖 Header、Ribbon 和 StatusBar 内部的菜单项、快捷入口、页签、分组和显示项。自定义项目只能引用宿主注册的 Command ID、状态 Binding 或已选远程组件的 Renderer;禁止在 JSON 中保存函数、表达式或任意脚本。跨组件状态、事件、数据处理与业务编排由受治理插件实现,产品工程保留启动、安全边界、能力注入和失败回滚。

插件清单、产品插件计划、执行阶段、稳定拓扑排序和上下文权限见插件运行时契约规范。

tactics-sim-engine 的中间工作区模板固定为 left-center-right,并提供 left、center、right、bottom 四个插槽;弹窗、抽屉和浮层通过 overlays 装配。其他产品可以在自己的产品契约版本中采用不同 Shell,但不能由远程组件接管产品基座。

Component Artifact ​

制品必须包含:

  • ESM 入口;
  • component.manifest.json;
  • 可选 CSS 与静态资源;
  • 精确 SemVer;
  • 至少一个 Renderer;
  • 所需 Capability Port 和兼容版本;
  • Config Schema 或可验证 Props 契约;
  • 宿主 externals 声明;
  • 发布时生成的完整性信息。

Product Manifest ​

产品清单必须包含:

  • schemaVersion、产品 ID 和精确版本;
  • Runtime 精确版本;
  • App 精确版本与不可变入口;
  • 页面和布局节点;
  • Renderer、Container、Theme、Props;
  • 产品注册的 Capability Binding;
  • 可选的制品完整性值。

清单禁止包含函数源码、任意表达式、密钥、Token 和复杂工作流脚本。

强制职责边界 ​

Remote App 必须 ​

  • 使用 Vue 3 <script setup lang="ts">;
  • 从公共入口导出,禁止要求消费者访问内部路径;
  • 用明确 Renderer 表达卡片/列表/树/表格等形态;
  • 使用语义 Design Token,避免硬编码产品品牌样式;
  • 通过 Props、Events、Slots/扩展点和 Capability Port 交互;
  • 对 loading、empty、error、permission-denied 状态有可测试表现;
  • 清理事件监听、订阅、定时器和外部资源;
  • 将 Vue、Cesium、Naive UI 等宿主级依赖设为 external/peer。
  • 远程单文件 ESM 使用 @nexa/component-build-tools 将宿主依赖改写到冻结的 globalThis.__NEXA_HOST__.libraries,并声明精确 hostContract;禁止自行写入 Host 全局对象。

Remote App 禁止 ​

  • import products/* 或产品别名;
  • 直接读取产品 Pinia Store;
  • 直接调用客户后端 URL;
  • 组件之间直接 import 形成隐式协同;
  • 把业务编排塞入 Renderer;
  • 用 latest 或浮动范围描述运行依赖;
  • 在 Manifest 或日志中存储密钥。

Product 必须 ​

  • 提供稳定基础能力、插件运行时和安全边界,由插件代码实现工作流、状态机、数据处理和跨 App 协同;
  • 通过 Adapter 把客户 DTO/API 转换成稳定 Port;
  • 在加载组件前校验 Manifest 和能力兼容性;
  • 为远程加载提供超时、错误边界、重试上限和可理解的降级界面;
  • 固定组件与 Runtime 版本,记录可回滚的发布快照;
  • 对远程 App 组合执行契约测试和关键业务集成测试。

Product 禁止 ​

  • 把复杂业务规则写成 JSON 表达式;
  • 信任远程 Manifest 中未声明的入口或能力;
  • 让页面组件直接完成跨 App 事务;
  • 因远程加载而删除 Runtime 所需的 Service、Store、Event 或 Capability Adapter;
  • 将所有基础依赖远程化。

形态定制规则 ​

text
能力相同、表现不同     → Renderer
表现相同、主题不同     → Theme Token
外壳不同               → Container
内容略有增减           → Props / Slot / Extension
产品协同逻辑不同       → Plugin
业务语义不同           → 新 App / Capability

Renderer 名称使用小写短横线格式,例如 card、compact-list。Container 只能从运行时支持的白名单中选择:plain、card、panel、dock、dialog、drawer、floating。

数据适配规则 ​

组件依赖稳定 Port:

ts
export interface EntityDataPort {
  query(input: EntityQuery): Promise<EntityPage>
  getById(id: string): Promise<EntitySummary>
}

产品实现 Adapter:

ts
export class CustomerEntityAdapter implements EntityDataPort {
  public async query(input: EntityQuery): Promise<EntityPage> {
    const response = await this.api.queryEntities(toCustomerQuery(input))
    return toEntityPage(response)
  }
}
  • URL、环境、租户、超时:环境配置;
  • 字段重命名、简单路径转换:受限声明映射;
  • 聚合、事务、权限、订阅、补偿:插件代码通过受治理 Capability Port 完成;
  • 后端 DTO:产品 Adapter 内部;
  • 跨产品领域模型与 Port:底层 Contract/Schema;
  • 语义不同:拆分能力,不做万能 Adapter。

产品推荐目录 ​

text
src/
├── bootstrap/          # 清单加载、运行时初始化
├── business/           # 产品用例与业务规则
├── plugins/            # 产品内置插件入口(远程化前的过渡形态)
├── adapters/           # 客户 API/DTO → Capability Port
├── services/           # GIS、仿真、算法等服务
├── stores/             # 产品状态
├── runtime/            # 运行时产品侧集成
├── layouts/            # 布局宿主
└── components/         # 产品专属 UI/扩展点

目录按真实需求渐进创建,禁止预建空壳。

版本与兼容 ​

  • 所有清单版本必须是 x.y.z 精确 SemVer;
  • Capability 使用独立版本并遵循 SemVer;
  • Breaking Change 必须走弃用周期并提供迁移;
  • 产品发布锁定 productVersion/runtimeVersion/apps/schemas/environment;
  • 旧制品路径不可覆盖;紧急封禁必须记录原因和受影响产品;
  • Runtime 至少兼容当前和上一版 Manifest Schema,移除兼容需单独评审。

安全与可靠性 ​

  • 生产入口必须 HTTPS,域名进入允许列表;装配文件可使用由产品 HTTPS 网关代理的同源 /api/... 内容寻址路径,本地开发仅允许 loopback HTTP;
  • 发布流水线生成 hash/SRI,运行时验证后执行;
  • 配置 CSP,禁止任意源脚本;
  • 每个 App 有独立错误边界和加载超时;
  • 管理平台不可用不得影响已发布且已缓存/可访问制品的产品;
  • 组件下架、删除和紧急封禁是不同状态;删除已被产品引用的制品必须被服务端拒绝;
  • 运行时记录组件 ID、版本、加载耗时和错误,不记录敏感数据。

自动门禁 ​

运行:

bash
pnpm check:composition

门禁自动发现仓库中的 component.manifest.json、product.manifest.json 和规范示例,并检查:

  • Schema/示例可解析;
  • 必填字段与标识格式;
  • 精确版本;
  • Renderer、入口、能力绑定完整;
  • 禁止 latest 和浮动版本;
  • Product 内组件实例 ID 唯一;
  • Product Lock 与 Manifest 使用相同的精确产品、Runtime 和组件版本;
  • 生产远程入口使用 HTTPS;
  • 宿主 externals 不得被声明为 bundled。

变更检查表 ​

  • [ ] 明确此次差异属于 Renderer、Theme、Container、Props、Adapter 还是 Plugin;
  • [ ] 未把产品业务逻辑放进远程 App;
  • [ ] 未让远程 App 直接调用客户 API 或产品 Store;
  • [ ] Manifest 使用精确版本和不可变入口;
  • [ ] 新 Port 有版本、类型、错误语义和契约测试;
  • [ ] 新 Renderer 有 loading/empty/error/permission 测试;
  • [ ] Product Adapter 覆盖 DTO 转换、错误映射和取消/超时;
  • [ ] 远程加载失败有降级和回滚方案;
  • [ ] pnpm check:composition 通过;
  • [ ] 全仓必跑命令通过。