山海万灵 HarmonyOS 文化知识实战(07):Repository Factory 的 HTTP/Mock/Fallback 切换

📅 2026/8/4 11:53:03
山海万灵 HarmonyOS 文化知识实战(07):Repository Factory 的 HTTP/Mock/Fallback 切换
文化图鉴的首页、探索页和馆长讲解页都要读取神兽、区域、展厅和学习卡。开发环境切换到 Mock 服务或网络短暂不可用时如果页面直接判断接口地址很容易出现一套在线 UI、一套离线 UI。山海万灵把这种差异收敛在 Repository Factory 中页面始终面向同一个ShanhaiRepository契约工作。一、页面只取得领域仓库ShanhaiAppViewModelFactory创建 ViewModel 时调用createShanhaiRepository()。页面和 ViewModel 不直接构造 HTTP 客户端也不关心当前数据来自服务还是内置资料它们只调用loadBootstrap、listBeasts、listRegions、explainBeast等领域方法。export function createShanhaiRepository(): ShanhaiRepository { return new FallbackShanhaiRepository( new HttpShanhaiRepository(), new MockShanhaiRepository() ) } export function createShanhaiAppViewModel(): ShanhaiAppViewModel { return new ShanhaiAppViewModel( createShanhaiRepository(), localProgressRepository, userProfileRepository ) }这种边界让 UI 接收的始终是神兽、区域、展厅和学习卡等领域对象。服务端字段调整、Mock 数据补充和网络异常处理都留在仓库层页面不会因此增加分支。二、首次引导决定主通道FallbackShanhaiRepository把 HTTP Repository 作为 primary把 Mock Repository 作为 fallback。应用首次加载时先请求loadBootstrap()成功后将primaryReady置为真后续的目录和概览读取走 HTTP 通道。请求失败则立即读取内置 Mock 数据应用仍能打开图鉴和探索入口。async loadBootstrap(): PromiseShanhaiBootstrap { try { const bootstrap await this.primary.loadBootstrap() this.primaryReady true return bootstrap } catch (_) { this.primaryReady false return this.fallback.loadBootstrap() } } isUsingLocalFallback(): boolean { return !this.primaryReady }项目自带 Mock API 联通后首页会回读“在线同步 / 内容与图谱已同步”。这个提示来自同一套启动数据链路说明当前启动流程已经取得 primary 返回的内容投影。三、目录读取与细粒度动作分开处理目录类方法包括listBeasts、listRegions、listHalls、listAiGuides和getOverview。当首次引导已经建立 HTTP 主通道时这些方法沿用该通道首次引导未成功时则统一使用 Mock 数据。这样首页所需的基础目录在一次引导后有明确的数据来源。场景仓库选择页面表现bootstrap 请求成功HTTP Repository显示在线同步加载远端目录和概览bootstrap 请求失败Mock Repository保留本地图鉴目录与探索入口讲解、学习卡、图谱推荐请求失败自动回退到 Mock Repository展示可读的本地讲解或学习卡不让目标页面白屏四、交互型请求具备二次回退神兽讲解、学习卡、图谱推荐和发现动作属于进入页面后的细粒度请求。这些方法会在 HTTP 调用失败时把primaryReady改回假并改由 Mock Repository 返回同形状的领域对象。页面不用捕获网络异常来拼装替代内容。async explainBeast(beastId: string, topic: CuratorGuideTopic): PromiseAiGuideItem { if (this.primaryReady) { try { return await this.primary.explainBeast(beastId, topic) } catch (_) { this.primaryReady false } } return this.fallback.explainBeast(beastId, topic) } async listGraphRecommendations(nodeType: string, nodeId: string) { if (this.primaryReady) { try { return await this.primary.listGraphRecommendations(nodeType, nodeId) } catch (_) { this.primaryReady false } } return this.fallback.listGraphRecommendations(nodeType, nodeId) }回退状态会保留到下一次引导成功为止。用户继续阅读时页面看到的是同一类神兽讲解、学习卡或推荐项而不是错误页这也是 Repository 契约统一的直接收益。五、HTTP 映射层消化服务端字段HTTP Repository 负责把接口响应映射为ShanhaiRepository使用的模型。新增字段或字段命名变化先在这里归一化再由 Mock Repository 提供同一模型的本地版本。ViewModel 不依赖原始 JSON 字段因此在线数据与 Mock 数据能使用同一套列表、详情和推荐组件。这条约束也明确了边界业务页只消费已经映射的领域对象网络地址、请求失败和服务端字段兼容不进入 ArkUI 页面。需要接入新的服务时先补齐 HTTP 映射和 Mock 对照数据再让页面使用新增的领域字段。以神兽详情为例HTTP 数据除了基础名称和描述还会映射区域、展厅、学习卡与图谱关联。Mock 数据采用相同的BeastItem、AiGuideItem和GraphRecommendationItem形状详情页只按这些模型渲染。这样服务端将嵌套字段改为可选字段时可以在 HTTP Repository 中补默认值或兼容转换页面没有必要根据接口版本再增加条件判断。async listLearningCards(beastId: string): PromiseBeastLearningCard[] { if (this.primaryReady) { try { return await this.primary.listLearningCards(beastId) } catch (_) { this.primaryReady false } } return this.fallback.listLearningCards(beastId) }这里的回退边界也应保持明确首次loadBootstrap失败时本地目录成为启动数据已经成功建立主通道后讲解、学习卡、图谱推荐和发现动作会捕获单项失败并回退。目录读取不在这个细粒度捕获列表中因而其异常策略不能由页面临时补齐。新增领域方法时应先决定它属于启动目录还是交互动作再选择对应的失败路径。六、验收动作可先启动项目自带的 Mock API再启动应用并进入首页确认在线同步提示与图鉴、探索入口都已加载。随后进入神兽详情并触发讲解、学习卡或图谱推荐当交互请求不可用时页面应继续显示本地可读内容。恢复服务后重新引导目录读取重新进入 HTTP 通道。验收时关注的是同一条操作链上的可观察结果启动成功后首页展示在线同步状态进入详情后讲解或学习卡能够渲染人为使该交互请求失败后仍返回本地同类型内容重新完成启动引导后状态回到 HTTP 主通道。不要把其他页面的空状态当作回退成功也不要把旧安装包的画面与新构建包混在一次结果中。对于服务端演进可增加一组 Mock 契约测试给 HTTP 映射层一个字段缺失、空数组和未知枚举值的响应确认输出仍能构造领域模型再对 Mock Repository 做同一接口断言。两组结果一致才能让页面组件在切换数据源后继续按既有模型工作。网络请求接口的配置与调用方式可参考 HarmonyOS HTTP 请求指南。七、把数据源切换限制在一个入口Repository Factory 把 HTTP、Mock 与 Fallback 的组合固定在应用入口ViewModel 取得的是稳定的领域接口。首次引导负责确定主通道交互型请求负责在失败时回退本地数据负责维持图鉴可读性。后续增加新接口时只需扩展 Repository 契约、HTTP 映射与 Mock 对照数据页面结构无需为数据源切换再写一遍。