Appearance
ADR-001:NEXA 产品装配与远程 App 架构
状态
已接受(2026-08-22)
背景
NEXA 的产品面向不同客户交付。产品往往复用相同的 GIS、时间轴、实体编辑、底图管理等能力,但布局、视觉形态、数据接口和跨组件业务流程不同。当前产品通过 workspace 依赖和静态 import 集成 apps/*,每次调整组件组合都需要重新修改依赖和产品源码。
目标不是用配置替代复杂产品代码,而是缩短产品交付链路:复用成熟 App,通过配置完成组件选择、版本锁定、展示形态和初始布局,再由少量产品代码实现业务协同。
决策
NEXA 采用以下产品模型:
text
产品 = 产品壳 + 产品业务内核 + 产品装配清单 + 可远程加载 App职责边界如下:
- 远程 App 提供边界清晰的可复用能力,不拥有产品工作流,不直接依赖产品 Store,不直接绑定客户后端 API。
- 产品装配清单 固定 App 版本、Renderer、Container、Theme、初始 Props、能力绑定和页面布局,只表达声明式装配信息。
- 产品业务内核 通过 TypeScript 代码实现状态机、工作流、跨 App 协同、权限、事务、异常补偿和算法/GIS/仿真编排。
- 产品运行时 校验清单、解析远程制品、异步加载 JS/CSS、注册组件、注入能力、渲染布局并提供加载失败降级。
- 组件服务平台 管理不可变制品、版本、Manifest、兼容信息、发布、下架与回滚;管理 App 负责选择、预览和导出装配清单,但不承载已发布产品的运行依赖。
核心边界
配置与代码
配置负责“是什么”:组件、精确版本、Renderer、容器、主题、默认参数、初始布局、权限标识和能力绑定。
代码负责“怎么协同”:条件分支、状态机、多步骤操作、数据转换、事务、异步协调、错误补偿、性能策略和安全校验。
禁止把复杂业务逻辑扩展成 JSON 脚本语言。
能力与表现
同一能力的差异按以下规则归属:
| 差异 | 归属 |
|---|---|
| 卡片、列表、树、表格 | Renderer |
| 卡片、Dock、抽屉、弹窗、无外壳 | Container(产品运行时) |
| 明暗色、品牌色、圆角、密度 | Theme / Design Token |
| 字段、操作项、默认值 | Props / Config Schema |
| 局部产品内容 | 命名扩展点 / Slot |
| 跨组件业务协同 | 产品 Orchestration 代码 |
| 业务语义不同 | 新 App / 新 Capability |
禁止用大量互斥布尔 Props 构建万能组件。
数据接口
远程 App 只依赖稳定 Capability Port,不直接调用具体客户 API:
text
Remote App → Capability Port → Product Adapter → Customer API- 地址、租户、超时等环境差异可以配置;
- 简单字段和分页结构差异可以声明映射;
- 聚合调用、权限校验、事务、实时订阅和补偿必须由产品 Adapter/Service 代码实现;
- 后端 DTO 只存在于适配器边界,跨产品契约进入底层 Contract/Schema;
- 若领域语义不同,即使 UI 相似也不得强行共用业务 App。
依赖策略
产品不会取消全部 pnpm 依赖。Vue、Pinia、产品运行时、Contract、Cesium/GIS 核心、布局引擎、主题宿主等稳定基础设施仍为构建时依赖;经治理标记为 remote-ready 的可装配 apps/* 才能改为远程加载。
远程制品采用标准 ESM + Manifest。Vue、Cesium、Naive UI 等宿主级依赖必须 external/shared,避免多实例。是否引入 Module Federation 必须另行决策,不能成为 Manifest Contract 的组成部分。
发布与安全
- Manifest 必须锁定精确 SemVer,禁止
latest、next、^、~和范围版本; - 已发布制品 URL 必须不可变,生产环境使用 HTTPS;
- 制品应提供 SRI/hash,运行时实施来源白名单、CSP、超时、失败隔离和回退;
- 新版本不得覆盖旧版本路径;产品发布必须记录 product/runtime/apps/schemas/environment 版本;
- 已发布产品应能在管理平台暂时不可用时继续运行;
- 下架不等于删除,已锁定版本按保留策略继续可取,紧急封禁除外。
运行时交互模型
App 通过事件、命令和 Capability Port 与产品交互。禁止 App 互相直接 import 或访问产品内部 Store。
text
App Event → Product Orchestration → Store/Workflow → Capability Port → Target App/Service产品可在命名扩展点注册产品专属 Vue 组件,但扩展点必须由 App Manifest 声明,产品不得依赖 App 内部文件。
机器可读契约与门禁
- Component Manifest Schema:
packages/component-contracts/schemas/component-manifest.schema.json(契约单一真相源) - Product Manifest Schema:
packages/component-contracts/schemas/product-manifest.schema.json(契约单一真相源) - 强制规范:
docs/specs/product-composition.md - 自动门禁:
pnpm check:composition
任何真实平台、Runtime、Service 或新 Package 落地时,必须再按真实资产更新 architecture/*.yaml。本 ADR 不预先创建或登记不存在的能力。
后果
正面影响:产品可快速更换组件版本和布局;表现、数据和业务协同边界清晰;相同 App 可跨产品复用;产品仍保留复杂系统所需的编码能力。
代价:必须维护 Manifest/Port 版本、产品 Adapter、远程制品兼容矩阵和运行时故障治理;远程加载不会消除集成测试,只会改变集成边界。
后续阶段
- 设计组件服务平台的领域模型、API Contract、存储和发布状态机;
- 建立最小 Runtime PoC,仅迁移一个低风险 App;
- 建设制品服务与管理 App;
- 接入布局编辑、预览、发布、回滚;
- 经人工验收后逐个迁移现有 App,禁止一次性切换全部组件。