從行動客戶端視角設計一個可無限滾動、可離線瀏覽的 News Feed app。重點不在後端動態生成,而在 app 端的資料層、本地持久化、離線互動與滾動效能。
📋 Requirements
- 功能:瀏覽動態、按讚/分享、撰寫含 rich text 與圖片影片的貼文、點擊進入貼文細節、無限滾動、離線存取先前已載入內容。
- 規模:全球 5 億 MAU、網路環境不穩、需在低階裝置維持流暢。
- 非功能:可擴展、滾動與建立貼文的效能、網路不穩仍可用(快取提供離線)、以智慧快取省流量、離線動作恢復連線後最終一致。
- 範圍外:即時更新與推播、開啟前預抓、身分驗證(假設已登入)。
🧠 Core Ideas
TIP
核心是「本地資料庫當唯一真實來源 (SSOT) + UI 只讀本地、網路只寫本地 + 互動先寫本地佇列再同步 + cursor 分頁配合無限滾動」。
Client 與 backend 的互動都由 client 主動發起(擷取動態、建立貼文、取貼文細節、送互動),故採 HTTP with REST APIs、資料編碼用 JSON(見API 設計)。架構分 UI Layer 與 Data Layer 兩層,以 Unidirectional Data Flow (UDF) 與 reactive programming 形塑資料流,資料流向恆為 Backend → Local Storage → UI。
⚖️ Reference Design
API 契約與分頁
- 動態擷取
GET /v1/feed?after=&limit=30回傳FeedApiResponse;細節GET /v1/posts/{postId}回傳完整PostDetail;建立POST /v1/posts;互動POST /v1/posts/{postId}/interactions。 - List-Detail 兩層模型:列表用輕量
PostPreview(縮圖、只帶必要欄位省頻寬),點擊才載入完整PostDetail(高解析度、全附件)。 - 無限滾動採 cursor-based pagination,比 offset 更適合頻繁變動的動態(穩定順序、深層滾動不掉效能)。
postId、created_at由 backend 生成(source of truth);client 帶requestId供去重;互動請求不帶 user id,由 auth token 取得。
本地儲存與離線 (SSOT)
- 本地儲存選 relational database:貼文結構良好、需複雜查詢與排序、需一致性——是 mobile app 的預設首選。
- 離線用途:無網路時捲動已快取貼文、開啟即時顯示不等網路(見快取策略)。
- SSOT 原則:UI 只從本地 DB 讀、絕不直接讀網路;網路回應只更新本地 DB 再向上傳遞;登出時清除快取。
- 淘汰用 LRU + TTL + 最低門檻混合策略(起始:100–200MB、TTL 15 天),參數可由 backend 遠端調整。
Optimistic writes 與離線互動
- Optimistic write:不等 backend 確認就立即更新 UI;state holder 並行做「更新 UI state」與「通知 repository 寫本地並同步 backend」。
- 所有互動先寫本地——把本地 DB 當 queue,新增
UserInteractions表依發生順序記錄互動,恢復連線後由 repository 背景處理;成功同步後從該表移除並更新Posts表。 - 失敗用 exponential backoff 重試;達上限時:不重要動作回滾 UI,重要動作(如發文失敗)通知使用者。
Rich content 渲染與滾動效能
- 貼文內容以 HTML 編碼支援 rich text;後端送 HTML 字串,故 validation 與 sanitization 必不可少(防 XSS)。
- 渲染選將 HTML 解析成原生 UI 元件(而非 WebView):滾動效能更佳、手勢整合順暢。
- 修卡頓 (janky scrolling):view recycling 重用離開視區的 view、圖片/影片以 URL 非同步載入不阻塞主執行緒、扁平化 layout、避免不必要 re-render。
- 依裝置 CPU/記憶體/網路/電量動態調整媒體品質,backend 須提供同一媒體的多品質版本。
🔑 Takeaways
- 這是 client-side 題:設計重心在資料層——本地 DB 作 SSOT,UI 只讀本地、網路只寫本地。
- 離線靠本地 relational DB + 混合淘汰策略;互動先寫本地佇列,恢復連線後以 backoff 同步。
- Optimistic write 給即時回饋;關鍵資料 (
postId、時間戳) 由 backend 生成維持一致。 - 無限滾動用 cursor 分頁;rich content 用 HTML + 原生渲染;滾動流暢靠 view recycling、非同步媒體與動態品質調整。
No notes yet — jot your takeaways or Q&A here.