Appearance
产品装配与远程 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 / CapabilityRenderer 名称使用小写短横线格式,例如 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通过; - [ ] 全仓必跑命令通过。