シンプルなインターフェース、揺るがないルール:待ち合わせモバイルアプリを開発する
唯一の枠を保証し、サーバーの時刻に従い、ネットワークの切断に耐え、API にプライバシーを適用させなければならない iOS と Android のモバイルアプリの設計回顧。
シンプルなインターフェース、揺るがないルール
公共の場所。選んだ所要時間。二人の参加者。このアプリでは、写真やプロフィールを眺めることなく、近くでの活動を提案したり参加したりできる。意図的に絞り込んだこの形式の裏側で、私たちは唯一の枠、時刻、途切れる接続、全員に見えてはならない情報を扱うプロダクトを構築している。
背景
コーヒーを飲む、歩く、展覧会を見る。出発点は共通の活動と共通の空き時間である。一人が提案し、もう一人が参加する。二人は現地で初めて顔を合わせる。
このプロダクトは Scopes がモンペリエで設計・開発している。作業は、フローの設計、iOS と Android のアプリ、業務サービス、管理機能、紹介サイトに及ぶ。制約は、目立ちにくい状況--二人が同じ瞬間に予約する、ネットワークの応答がログアウト後に届く、リクエストが待機している間に待ち合わせが期限切れになる--を処理しながら、体験を読みやすく保つことだ。
これらの状況は、画面と同じくらいアーキテクチャを決定づける。
画面を増やす前にフローをモデル化する
提案のライフサイクルは、明示的な状態と遷移で記述される。公開、予約、到着、キャンセル、期限切れである。ルールは、ネットワークにもデータベースにも依存しない独立した TypeScript モジュールにまとめられている。モジュールは行為者の識別情報とサーバーが提供する時刻を受け取り、その操作が可能かどうかを判断する。
この分離により、アプリを起動せずにルールをミリ秒単位で検証できる。また、画面、HTTP ルート、自動処理が同じ状況を別々に解釈することも防ぐ。
API は Fastify と PostgreSQL の上に構築されている。モジュラーモノリスという選択は、業務操作を同一トランザクションの中に保ち、運用すべきサービスの数を抑える。検証の契約はクライアントと共有し、SQL アクセスはサーバー側に留める。
唯一の枠を保証する
二人が同時に「参加」を押した場合、枠を得られるのは一人だけでなければならない。判断は、対象の提案をロックする PostgreSQL トランザクションの中で行われる。したがって二番目のリクエストは、すでに予約済みの状態を読み直すことになる。
時刻の検証はロック取得後に行う。トランザクションを待っている間に開始時刻が過ぎた活動に、参加できてはならないからだ。
作成処理はネットワークの再試行も扱う。各リクエストは、その繰り返しを認識するためのキーを持つ。最初の作成が成功したのに応答が失われた場合、再試行は同じ提案を見つけ出す。二つ目の活動を公開することはない。これらの挙動は、本物の PostgreSQL データベース上で並行トランザクションを用いてテストされている。
自分の求めに応じて周辺を探す
アプリは React Native と Expo を使い、ネイティブの地図を入口にしている。ユーザーはエリア、検索半径、時間と所要時間の条件を選ぶ。PostGIS がサーバー側で地理検索を行い、距離はメートルで表現される。
位置情報は明示的な操作によって要求される。エリアを手動で選ぶこともできる。地図を動かしても必ずしも新しいリクエストは発生しない。ユーザーが表示中のエリアでの検索を確定する。このフローでは、バックグラウンドで移動を追跡する必要はない。
API にプライバシーを適用させる
ログインは電話番号と確認コードに基づく。フローは、他の参加者に見せる写真、プロフィール、実名を要求しない。それでもサービスが匿名になるわけではない。アカウントと参加のデータは、機能のために依然として必要である。
現地では、各自が「黄色いリュック」のようなその時の目印を登録できる。二つの目印は、双方が到着を宣言した後に開示される。このルールはサーバーの応答で適用される。画面でフィールドを隠すだけでは足りないからだ。活動の一覧にはこれらの情報は含まれない。
セッションは失効可能で、トークンはハッシュ化された形でデータベースに保存される。モバイル側では、非同期の応答はそれを要求したセッションに紐づけられる。アカウントの切り替え後に、古いリクエストがデータを再表示してはならない。
構築段階から運用を見込む
サポートには、モバイルアプリとは別の内部インターフェースがあり、独自のサーバー、認証、専用の SQL ロールを持つ。問い合わせは追跡、割り当て、解決でき、ユーザーが補足情報を寄せたときには再開できる。
機微な操作は履歴に残る。案件のバージョンにより、たとえば二人の担当者が同じ案件を扱っているときに、古くなった変更を検知できる。ネットワーク切断後の再試行も考慮されている。
開発は完全な機能単位で進む。業務ルール、API、永続化、インターフェース、そしてフローの検証である。単体テストは判断を、統合テストはデータベース制約を、インターフェースの試験はエラー、再試行、切断を検証する。模擬プロバイダーを使うシナリオは、実際のサービスと実機での試験とは区別されたままである。
残る作業
プロジェクトには、実装済みの作成と参加のフロー、地理検索、テスト可能なプライバシールール、サポートツールがある。今後の開発は、とりわけ通知、外部連携と実機でのフローの検証、そしてモバイル配信前のインターフェースの仕上げに向かう。
プロダクトの紹介サイトはすでに公開されている。欧州連合の公用語 24 言語で静的 HTML として生成されている。そのアニメーションは二台の電話を同期させてフローを説明する。これはプロダクトの表現であって、ネイティブアプリのキャプチャではない。サイトは広告トラッカーも訪問者側のアナリティクスツールも使わずに動作する。
この記事の執筆時点で、モバイルアプリは開発中である。したがって、利用実績や大規模展開の結果はまだ提示しない。この作業が記録するのは、より差し迫った要件である。ユーザーにとって簡単な操作を、そのルールをネットワーク、時計、インターフェースの偶然に委ねることなく実現することだ。
技術情報
使用技術: TypeScript、React Native、Expo、Fastify、PostgreSQL、PostGIS、管理画面には React と Vite、Vitest、Docker。静的サイトは HTML、CSS、JavaScript で、Node.js による生成と Railway でのホスティング。
範囲: プロダクト設計、アーキテクチャ、モバイルとサーバーの開発、管理機能、テスト、多言語サイト。
ステータス: Scopes のプロダクト。モバイルアプリは 2026 年に開発中。プロダクト名はこの記事では伏せている。ご要望に応じて共有する。
ネットワークに左右されてはならない業務ルールを持つモバイルアプリを準備中ですか。Scopes はモンペリエから iOS と Android のアプリを設計・開発しています。モンペリエでのモバイル開発のページをご覧いただくか、状況をお知らせください。