靈 HarmonyOS 文化知識(shí)實(shí)戰(zhàn)(07):Repository Factory 的 HTTP/Mock/Fallback 切換)
文化圖鑒的首頁(yè)、探索頁(yè)和館長(zhǎng)講解頁(yè)都要讀取神獸、區(qū)域、展廳和學(xué)習(xí)卡。開發(fā)環(huán)境切換到 Mock 服務(wù)或網(wǎng)絡(luò)短暫不可用時(shí)如果頁(yè)面直接判斷接口地址很容易出現(xiàn)一套在線 UI、一套離線 UI。山海萬(wàn)靈把這種差異收斂在 Repository Factory 中頁(yè)面始終面向同一個(gè)ShanhaiRepository契約工作。一、頁(yè)面只取得領(lǐng)域倉(cāng)庫(kù)ShanhaiAppViewModelFactory創(chuàng)建 ViewModel 時(shí)調(diào)用createShanhaiRepository()。頁(yè)面和 ViewModel 不直接構(gòu)造 HTTP 客戶端也不關(guān)心當(dāng)前數(shù)據(jù)來(lái)自服務(wù)還是內(nèi)置資料它們只調(diào)用loadBootstrap、listBeasts、listRegions、explainBeast等領(lǐng)域方法。export function createShanhaiRepository(): ShanhaiRepository { return new FallbackShanhaiRepository( new HttpShanhaiRepository(), new MockShanhaiRepository() ) } export function createShanhaiAppViewModel(): ShanhaiAppViewModel { return new ShanhaiAppViewModel( createShanhaiRepository(), localProgressRepository, userProfileRepository ) }這種邊界讓 UI 接收的始終是神獸、區(qū)域、展廳和學(xué)習(xí)卡等領(lǐng)域?qū)ο蟆7?wù)端字段調(diào)整、Mock 數(shù)據(jù)補(bǔ)充和網(wǎng)絡(luò)異常處理都留在倉(cāng)庫(kù)層頁(yè)面不會(huì)因此增加分支。二、首次引導(dǎo)決定主通道FallbackShanhaiRepository把 HTTP Repository 作為 primary把 Mock Repository 作為 fallback。應(yīng)用首次加載時(shí)先請(qǐng)求loadBootstrap()成功后將primaryReady置為真后續(xù)的目錄和概覽讀取走 HTTP 通道。請(qǐng)求失敗則立即讀取內(nèi)置 Mock 數(shù)據(jù)應(yīng)用仍能打開圖鑒和探索入口。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 }項(xiàng)目自帶 Mock API 聯(lián)通后首頁(yè)會(huì)回讀“在線同步 / 內(nèi)容與圖譜已同步”。這個(gè)提示來(lái)自同一套啟動(dòng)數(shù)據(jù)鏈路說(shuō)明當(dāng)前啟動(dòng)流程已經(jīng)取得 primary 返回的內(nèi)容投影。三、目錄讀取與細(xì)粒度動(dòng)作分開處理目錄類方法包括listBeasts、listRegions、listHalls、listAiGuides和getOverview。當(dāng)首次引導(dǎo)已經(jīng)建立 HTTP 主通道時(shí)這些方法沿用該通道首次引導(dǎo)未成功時(shí)則統(tǒng)一使用 Mock 數(shù)據(jù)。這樣首頁(yè)所需的基礎(chǔ)目錄在一次引導(dǎo)后有明確的數(shù)據(jù)來(lái)源。場(chǎng)景倉(cāng)庫(kù)選擇頁(yè)面表現(xiàn)bootstrap 請(qǐng)求成功HTTP Repository顯示在線同步加載遠(yuǎn)端目錄和概覽bootstrap 請(qǐng)求失敗Mock Repository保留本地圖鑒目錄與探索入口講解、學(xué)習(xí)卡、圖譜推薦請(qǐng)求失敗自動(dòng)回退到 Mock Repository展示可讀的本地講解或?qū)W習(xí)卡不讓目標(biāo)頁(yè)面白屏四、交互型請(qǐng)求具備二次回退神獸講解、學(xué)習(xí)卡、圖譜推薦和發(fā)現(xiàn)動(dòng)作屬于進(jìn)入頁(yè)面后的細(xì)粒度請(qǐng)求。這些方法會(huì)在 HTTP 調(diào)用失敗時(shí)把primaryReady改回假并改由 Mock Repository 返回同形狀的領(lǐng)域?qū)ο?。?yè)面不用捕獲網(wǎng)絡(luò)異常來(lái)拼裝替代內(nèi)容。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) }回退狀態(tài)會(huì)保留到下一次引導(dǎo)成功為止。用戶繼續(xù)閱讀時(shí)頁(yè)面看到的是同一類神獸講解、學(xué)習(xí)卡或推薦項(xiàng)而不是錯(cuò)誤頁(yè)這也是 Repository 契約統(tǒng)一的直接收益。五、HTTP 映射層消化服務(wù)端字段HTTP Repository 負(fù)責(zé)把接口響應(yīng)映射為ShanhaiRepository使用的模型。新增字段或字段命名變化先在這里歸一化再由 Mock Repository 提供同一模型的本地版本。ViewModel 不依賴原始 JSON 字段因此在線數(shù)據(jù)與 Mock 數(shù)據(jù)能使用同一套列表、詳情和推薦組件。這條約束也明確了邊界業(yè)務(wù)頁(yè)只消費(fèi)已經(jīng)映射的領(lǐng)域?qū)ο缶W(wǎng)絡(luò)地址、請(qǐng)求失敗和服務(wù)端字段兼容不進(jìn)入 ArkUI 頁(yè)面。需要接入新的服務(wù)時(shí)先補(bǔ)齊 HTTP 映射和 Mock 對(duì)照數(shù)據(jù)再讓頁(yè)面使用新增的領(lǐng)域字段。以神獸詳情為例HTTP 數(shù)據(jù)除了基礎(chǔ)名稱和描述還會(huì)映射區(qū)域、展廳、學(xué)習(xí)卡與圖譜關(guān)聯(lián)。Mock 數(shù)據(jù)采用相同的BeastItem、AiGuideItem和GraphRecommendationItem形狀詳情頁(yè)只按這些模型渲染。這樣服務(wù)端將嵌套字段改為可選字段時(shí)可以在 HTTP Repository 中補(bǔ)默認(rèn)值或兼容轉(zhuǎn)換頁(yè)面沒有必要根據(jù)接口版本再增加條件判斷。async listLearningCards(beastId: string): PromiseBeastLearningCard[] { if (this.primaryReady) { try { return await this.primary.listLearningCards(beastId) } catch (_) { this.primaryReady false } } return this.fallback.listLearningCards(beastId) }這里的回退邊界也應(yīng)保持明確首次loadBootstrap失敗時(shí)本地目錄成為啟動(dòng)數(shù)據(jù)已經(jīng)成功建立主通道后講解、學(xué)習(xí)卡、圖譜推薦和發(fā)現(xiàn)動(dòng)作會(huì)捕獲單項(xiàng)失敗并回退。目錄讀取不在這個(gè)細(xì)粒度捕獲列表中因而其異常策略不能由頁(yè)面臨時(shí)補(bǔ)齊。新增領(lǐng)域方法時(shí)應(yīng)先決定它屬于啟動(dòng)目錄還是交互動(dòng)作再選擇對(duì)應(yīng)的失敗路徑。六、驗(yàn)收動(dòng)作可先啟動(dòng)項(xiàng)目自帶的 Mock API再啟動(dòng)應(yīng)用并進(jìn)入首頁(yè)確認(rèn)在線同步提示與圖鑒、探索入口都已加載。隨后進(jìn)入神獸詳情并觸發(fā)講解、學(xué)習(xí)卡或圖譜推薦當(dāng)交互請(qǐng)求不可用時(shí)頁(yè)面應(yīng)繼續(xù)顯示本地可讀內(nèi)容?;謴?fù)服務(wù)后重新引導(dǎo)目錄讀取重新進(jìn)入 HTTP 通道。驗(yàn)收時(shí)關(guān)注的是同一條操作鏈上的可觀察結(jié)果啟動(dòng)成功后首頁(yè)展示在線同步狀態(tài)進(jìn)入詳情后講解或?qū)W習(xí)卡能夠渲染人為使該交互請(qǐng)求失敗后仍返回本地同類型內(nèi)容重新完成啟動(dòng)引導(dǎo)后狀態(tài)回到 HTTP 主通道。不要把其他頁(yè)面的空狀態(tài)當(dāng)作回退成功也不要把舊安裝包的畫面與新構(gòu)建包混在一次結(jié)果中。對(duì)于服務(wù)端演進(jìn)可增加一組 Mock 契約測(cè)試給 HTTP 映射層一個(gè)字段缺失、空數(shù)組和未知枚舉值的響應(yīng)確認(rèn)輸出仍能構(gòu)造領(lǐng)域模型再對(duì) Mock Repository 做同一接口斷言。兩組結(jié)果一致才能讓頁(yè)面組件在切換數(shù)據(jù)源后繼續(xù)按既有模型工作。網(wǎng)絡(luò)請(qǐng)求接口的配置與調(diào)用方式可參考 HarmonyOS HTTP 請(qǐng)求指南。七、把數(shù)據(jù)源切換限制在一個(gè)入口Repository Factory 把 HTTP、Mock 與 Fallback 的組合固定在應(yīng)用入口ViewModel 取得的是穩(wěn)定的領(lǐng)域接口。首次引導(dǎo)負(fù)責(zé)確定主通道交互型請(qǐng)求負(fù)責(zé)在失敗時(shí)回退本地?cái)?shù)據(jù)負(fù)責(zé)維持圖鑒可讀性。后續(xù)增加新接口時(shí)只需擴(kuò)展 Repository 契約、HTTP 映射與 Mock 對(duì)照數(shù)據(jù)頁(yè)面結(jié)構(gòu)無(wú)需為數(shù)據(jù)源切換再寫一遍。