Upgrade to Pro — share decks privately, control downloads, hide ads and more …

2つのフロントエンドと状態管理

 2つのフロントエンドと状態管理

本資料は2025/8/28に開催された「MIXI MEETUP! ーTECH & DESIGN DAYー」における、開発本部 川瀬の資料です。

MIXI MEETUP!ー TECH & DESIGN DAYー
https://mixi.connpass.com/event/363501/

Avatar for MIXI ENGINEERS

MIXI ENGINEERS PRO

September 08, 2025
Tweet

Video

More Decks by MIXI ENGINEERS

Other Decks in Technology

Transcript

  1. アジェンダ 1. プラットフォーム特性の理解 WebとMobileの本質的な違い 2. Web: サーバー同期を前提とした設計 TanStack Queryによる状態管理 3.

    Mobile: 長期稼働とオフラインへの対応 独自のアーキテクチャによる状態管理 4. 実装の詳細と得られた知見 実際の実装例と技術選定の教訓 5. まとめ ユーザー体験最優先のアーキテクチャ選択
  2. SNSプラットフォーム「mixi2」 2024/12/16: Mobile版 (iOS, Android) をリリース • Flutter 3 •

    Riverpod v2 • 追加の状態管理の仕組み 2025/05/16: Web版を後続リリース • Next.js 15 • TanStack Query v5 SNS『mixi』誕生から20年、MIXIの新しいSNS「mixi2」サービス開始! | ニュース| 株式会社MIXI https://mixi.co.jp/news/2024/1223/37672/ 広がる「mixi2」、PCへ!ブラウザ版サービス提供開始 | ニュース| 株式会社MIXI https://mixi.co.jp/news/2025/0516/41189/
  3. ざっくりシステムアーキテクチャ 全体構成 • WebとMobileの2つのフロントエンド • APIサーバーはGoで実装 • Protocol BuffersでRPCの型定義を共有 ◦

    Web: Connect, Mobile: gRPC 詳細は Software Design 2025年9月号 新連載「技術選定の舞台裏」【1】mixi2 にて Software Design 2025年9月号 | 技術評論社 https://gihyo.jp/magazine/SD/archive/2025/202509
  4. 根本的な違い: ライフサイクルとメモリ管理 Webアプリケーション • 各タブが分離されたメモリ空間 • ページ遷移するとコンポーネント再マウント • リロードですべての状態を再構築 Mobileアプリケーション

    • 起動から終了まで同一プロセス • メモリ上のオブジェクトを保持しながら画面遷移 • バックグラウンド実行: OSが許す限り状態を維持
  5. ライフサイクルが生む問題 • 同一インスタンスを共有 • メモリ管理が重要 • 状態の一貫性が必要 • コンポーネントは画面遷移で破棄 •

    意図的な状態管理が必要 Mobile: アプリ単位の ライフサイクル Web: コンポーネント単位の ライフサイクル
  6. Web: TanStack Queryの選択 サーバー状態同期に特化したライブラリ • サーバーが常にSingle Source of Truth •

    キャッシュはパフォーマンス最適化 • @connectrpc/connect-queryでRPCとシームレスに統合 • バックグラウンド再検証で常に最新化
  7. 役割分担による連携 それぞれの役割 • グローバル状態(Riverpod) ◦ 認証、設定、ナビゲーション • コンテンツデータ(ObjectWatchService) ◦ 投稿、コメントの管理と監視

    • イベント通知(EventHub) ◦ 画面間の更新通知 ポイント • 各層が独立して責務を持つ • 必要に応じて連携 • テストが書きやすい設計
  8. Observerパターンの動作 動作フロー 1. ウォッチャー登録: Widgetがオブジェクトを監視開始 2. 更新通知: オブジェクト変更時に全ウォッチャーへ通知 3. 即座提供:

    新規ウォッチャーにキャッシュがあれば即提供 効果 • O(1)更新: 変更されたオブジェクトのみ再描画 • データ整合性: 全画面で同一データを参照
  9. ObjectWatchServiceの設計思想 Riverpodとの統合における課題 アーキテクチャ要件と設計判断 • 監視の自動解放 ◦ 課題: 手動管理は危険 ◦ 解決策:

    Widgetライフサイクルと統合 • 画面単位の管理 ◦ 課題: 複数Widget連携 ◦ 解決策: ScreenCache機構 • データ一貫性 ◦ 課題: 古いデータ問題 ◦ 解決策: 最新データ自動取得 解決策: Widget生存期間とキャッシュ分離
  10. プラットフォーム特性との合致 Webの特性 • 短命ライフサイクル: ページ遷移で破棄 • サーバー中心: 常にAPIから最新データ • 自動メモリ管理:

    JavaScriptエンジンのGC TanStack Queryが最適な理由 • クエリキーで自動更新: 同じキーの全コンポーネントが更新 • ページ遷移でクリーンアップ: メモリリークの心配なし • サーバー同期特化: キャッシュは一時的
  11. 両者の比較: 異なる問題を解決 • 主目的: クライアント内共有 • データ源: メモリ内オブジェク ト •

    更新契機: オブジェクト変更 • メモリ管理: 参照カウント • オフライン: 完全対応 • 主目的: サーバー状態同期 • データ源: サーバーAPI • 更新契機: APIレスポンス • メモリ管理: ページ単位 • オフライン: 非対応 ObjectWatchService TanStack Query
  12. データフローの比較 • オフラインファースト • ローカルがSSOT • バックグラウンド同期 • 常にサーバーと同期 •

    キャッシュは一時的 • 楽観的更新でUX向上 Mobile: クライアント中心 Web: サーバー中心
  13. Mobile: オフライン対応のデータ保存 用途別の3層構造 1. 高速アクセス用 a. ObjectWatchService + Riverpod(メモリ) 2.

    オフライン用 a. Isar Database(ローカルDB) 3. 認証情報用 a. flutter_secure_storage(暗号化)
  14. 実例: 「いいね」機能の実装比較 同じUX、異なる実装戦略 ユーザー体験 • ボタンタップ → 即座にUI更新 → サーバー同期

    実装アプローチ • Web: 楽観的更新 + ロールバック • Mobile: ローカル優先 + バックグラウンド同期
  15. アーキテクチャ選択の教訓 実践から学んだ3つの原則 1. 無理に統一しない a. Web/Mobile別々の最適解を選ぶ b. プラットフォームの強みを活かす 2. 車輪の再発明を恐れない

    a. 必要なら作る勇気を持つ b. 既存の解決策に固執しない 3. ユーザー体験優先 a. 技術的な美しさより実用性 b. プラットフォーム特性を最大限活用
  16. Web: ステートレス → サーバー同期型 • ページ遷移で破棄 ◦ → TanStack Query

    • 常時接続 ◦ → 楽観的更新 • ブラウザGC ◦ → メモリ管理不要
  17. Web: TanStack Queryが最適だった理由 実際の課題 • ページ遷移のたびにAPIを再呼び出し • 同じデータを複数コンポーネントで重複取得 • 楽観的更新の実装が複雑

    TanStack Queryで解決 • クエリキー: 自動キャッシュ共有 • 宣言的: コード量を大幅削減 • mutation: 楽観的更新の標準化 → サーバー同期型アーキテクチャの確立
  18. Mobile: Riverpod + ObjectWatchService 役割を分けた理由 • Riverpod: グローバル状態(認証等)に最適 • 大量コンテンツ:

    専用の仕組みが必要 • 画面間同期: 複雑な要件への対応 ObjectWatchServiceで実現 • 細粒度更新: オブジェクト単位 • 自動同期: 画面横断的 • データ維持: スクロール時も最新 → クライアント共有型アーキテクチャの確立
  19. 学び 問題を正確に理解し、それを解決する技術を選ぶ 実践した原則 1. 問題の本質を見極める a. プラットフォーム特性の理解 b. ユーザー体験の要件定義 2.

    既存ツールを評価する a. 要件との適合性を検証 b. 制約と限界を認識 3. 必要なら作る a. プラットフォーム特性に合わせた設計 b. 既存の解決策で不十分なら自ら作る