メインコンテンツまでスキップ

ADR-0004: Expoによるモバイルアプリ開発

ステータス

Accepted - 2024年採用

コンテキスト

Bazbiiのモバイルアプリケーション(user-mobile)を開発する際、以下の要件がありました:

  • クロスプラットフォーム: iOSとAndroidの両方に対応したい
  • 開発効率: 高速な開発サイクルとホットリロード
  • ネイティブ機能: 位置情報、セキュアストレージ、暗号化などのネイティブ機能が必要
  • モノレポ対応: pnpm workspaceとの統合
  • 型安全性: TypeScriptでの開発

以下の課題がありました:

  • ネイティブビルドの複雑さ: React Nativeのネイティブビルド環境構築が複雑
  • 開発環境の統一: チームメンバー間で開発環境を統一しやすい方法が必要
  • デプロイの容易さ: テスト配布やビルドプロセスの簡素化

決定

Expo(Managed Workflow)を採用し、以下の構成を実現しました:

client-apps/user-mobile/
├── app.json # Expo設定
├── package.json # Expo SDK 54.0.0
├── metro.config.js # Metro bundler設定(モノレポ対応)
└── src/ # React Nativeアプリケーション

実装方針

  1. Expo SDK 54.0.0: 最新の安定版を使用
  2. React Navigation: Expo Routerではなく、React Navigationを採用(柔軟性を優先)
  3. Expo Modules: ネイティブ機能はExpo Modules(expo-location, expo-secure-store等)を使用
  4. 新アーキテクチャ: React Nativeの新アーキテクチャを有効化(newArchEnabled: true
  5. モノレポ統合: Metro bundlerの設定でpnpm workspaceに対応

使用しているExpo機能

  • expo-location: 位置情報取得
  • expo-secure-store: セキュアなストレージ(JWTトークン保存)
  • expo-crypto: 暗号化機能
  • expo-localization: 国際化対応
  • expo-splash-screen: スプラッシュスクリーン
  • expo-status-bar: ステータスバー制御

考慮した代替案

代替案1: React Native CLI(Bare Workflow)

  • メリット:
    • ネイティブコードへの完全なアクセス
    • カスタムネイティブモジュールの容易な追加
    • Expoの制約を受けない
  • デメリット:
    • 開発環境構築が複雑(Xcode、Android Studioの設定)
    • ビルドプロセスが複雑
    • チーム間での環境統一が困難
  • 判断: MVP開発では開発効率を優先し、却下

代替案2: Expo Router

  • メリット:
    • ファイルベースのルーティング
    • 統合されたナビゲーション
  • デメリット:
    • 既存のReact Navigationとの互換性
    • 学習コスト
    • 柔軟性がやや制限される
  • 判断: 既存のReact Navigationの知識と柔軟性を優先し、却下

代替案3: Flutter

  • メリット:
    • 高いパフォーマンス
    • 統一されたUI
  • デメリット:
    • Dart言語の学習コスト
    • 既存のReact/TypeScript資産の再利用ができない
    • バックエンドとの型共有が困難
  • 判断: TypeScriptエコシステムとの統合を優先し、却下

影響

メリット

  • 開発効率: Expo Goアプリによる即座のテスト、ホットリロード
  • 環境構築の簡素化: 開発環境のセットアップが簡単(Node.jsとExpo CLIのみ)
  • ネイティブ機能へのアクセス: Expo Modulesによる統一的なAPI
  • ビルドプロセス: EAS Build(将来使用可能)によるクラウドビルド
  • モノレポ対応: Metro bundlerの設定でpnpm workspaceと統合
  • 型安全性: TypeScriptとの統合
  • デプロイの容易さ: OTA(Over-The-Air)更新の可能性

デメリット・課題

  • ⚠️ ネイティブコードの制約: カスタムネイティブモジュールの追加に制限(開発ビルドが必要)
  • ⚠️ バンドルサイズ: Expo SDKを含むため、アプリサイズがやや大きくなる可能性
  • ⚠️ バージョンアップ: Expo SDKのアップグレード時に互換性の問題が発生する可能性
  • ⚠️ デバッグ: ネイティブコードのデバッグがやや複雑

運用上の考慮事項

  • 開発ビルド: カスタムネイティブモジュールが必要になった場合、開発ビルドへの移行
  • EAS Build: 本番ビルドはEAS Buildを検討(将来的に)
  • OTA更新: 軽微な更新はOTAで配信可能

実装の詳細

モノレポ統合

// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const config = getDefaultConfig(projectRoot);

// ワークスペースルートを認識
config.watchFolders = [workspaceRoot];
config.resolver.nodeModulesPaths = [
path.join(projectRoot, 'node_modules'),
path.join(workspaceRoot, 'node_modules'),
];

// pnpm / symlink対応
config.resolver.unstable_enableSymlinks = true;

新アーキテクチャ

// app.json
{
"expo": {
"newArchEnabled": true,
// ...
}
}

React Nativeの新アーキテクチャ(Fabric、TurboModules)を有効化し、パフォーマンス向上と将来の互換性を確保。

今後の検討事項

  • カスタムネイティブモジュール: 必要な場合は開発ビルドへの移行を検討
  • EAS Build: 本番環境でのビルド自動化
  • OTA更新: 軽微な修正の配信方法
  • Expo SDKアップグレード: 定期的なアップグレード計画

関連ADR

参考