FuelBuddyのカスタマーアプリはProgressive Web Appとして始まりました。Androidではうまく動作しましたが、iOSではプラットフォームの制約に直面しました。WebKitエンジンの制約、ホーム画面への追加が不安定なこと、限られたストレージ、そしてプッシュ通知機能の欠如です。FuelBuddyにとってこれは、PWAではiOSユーザーに対して信頼できるサービスを提供できず、安定したモバイル体験を提供するにはネイティブアプリが必要であることを意味しました。私は、アプリをReact Nativeで書き直したエンジニアリングチームの一員でした。
取り組んだこと
- PWAからReact Nativeへ: アプリをコンパイル済みのReact Nativeアプリケーションとして書き直し、8週間で14のユーザーフロー全体にわたって100%の機能パリティを達成し、iOSとAndroidの両方で一貫したネイティブの操作感を確保しました。
- スタック全体をまたいだ作業: モバイルフロントエンドに注力しつつ、Hasura GraphQLエンジン上で必要なCRUD APIを直接構築し、機能開発がバックエンドの依存関係でブロックされないようにしました。
- チームでのリリース: フロントエンドエンジニアやプロダクトマネージャーと密に連携し、要件をテスト済みの機能へと落とし込み、本番リリースのサイクルを予定通りに保ちました。
なぜ意味があったか
React Nativeへの移行により、iOSとAndroidの両方にまたがる信頼できる単一のコードベースが確立されました。8週間で14のユーザーフローを完全なパリティで出荷したことで、FuelBuddyはプラットフォームごとに別々の実装を保守することなく、モバイルユーザー基盤全体にサービスを提供できるようになりました。
チームとオーナーシップ
私はこの書き直しに携わったエンジニアの一人で、フロントエンドエンジニアやプロダクトマネージャーと共に、チームが移行に設定した8週間という期間内に14のフローすべてで完全なパリティを達成しました。
GraphQL層
CRUD APIはHasura上で動作し、Apolloではなくurqlを通じて消費されます。GraphQL Codegenが型付けされた操作を生成し、73個の独立した.graphqlファイルがアプリの実際の範囲をカバーしています。ローカルの状態管理はZustandを通じて行われ、その裏側には高速なキーバリューストアであるMMKVがあり、境界部分でのデータ検証はZodが担っています。
CRUDだけでなく位置情報と決済も
配送追跡は、単なる地図上のマーカーではなく、react-native-maps、専用のジオロケーションサービス、そして実際の地理空間計算(距離、範囲、経路計算)のためのTurf.jsを取り込んでいます。決済は2つの独立したゲートウェイを通じて動作します。ひとつはEasebuzzで、レジストリの依存関係ではなくベンダリングされたローカルパッケージとして統合されています。もうひとつはAxis Bankで、そのAPIはすべてのリクエストにAES-256-CBC(PBKDF2由来の鍵)とHMAC-SHA512による署名を要求します。これはJSの暗号ライブラリではなく、手書きのネイティブAndroidモジュール(NdpsAESLibrary、NativeModulesを介してJSにブリッジ)で実装されています。決済ゲートウェイの署名要件は、RN層が汎用的に満たせる類のものではないためです。認証と分析の両方はFirebaseがカバーしています。
テーブルだけでなくレポートも
レポートフローは、配送データとビジネスデータをVictory Nativeを通じてグラフとして描画し、同じデータをCSVにエクスポートします。json2csvでエンコードし、Android 10以降でアプリがファイルシステムへの直接アクセスを失って以来必須となったAndroidのScoped Storage API(react-native-scoped-storage)を通じてディスクに書き込み、ネイティブの共有シートを通じて受け渡します。
データモデルと規模
- 13〜14個の独立したフローコンテナ(住所、資産、ビジネス、配送、ホーム、位置情報、注文、集荷、レポート、設定、サポート、ユーザー、ウォレット)がアプリのナビゲーションを構成しており、「14のユーザーフロー」という数字と一致します。
- 銀行レベルのリクエスト署名は専用のネイティブAndroidモジュールが担い、それ以外はすべてReact Nativeのブリッジの上でJS/TypeScriptのままです。
Stack
React Native、GraphQL Code Generatorを伴うurql、状態管理にZustand、ストレージにMMKV、検証にZod、react-native-maps、地理空間計算にTurf.js、決済にEasebuzzとAxis Bank(後者はカスタムのネイティブAES/HMAC署名モジュールの裏側で動作)、レポーティングのグラフにVictory Native、認証と分析にFirebase、そしてHasura GraphQL上に構築。