📱

2024年|CANARY アプリの技術スタック

2024/05/10に公開

はじめに

こんにちは。カナリーでソフトウェアエンジニアをしている @snamiki1212 です。

私たちは 【もっといい「当たり前」をつくる】 をミッションに掲げている不動産テックカンパニーです。弊社では、現在下記のプロダクトを運用しています。

  • CANARY」: BtoC のお部屋探しポータル(アプリ/Web)
  • CANARY Cloud」: BtoB SaaS(不動産の仲介会社様向けの顧客管理システム)

この記事では、 CANARY のアプリで利用している技術スタックについてまとめています。

https://prtimes.jp/main/html/rd/p/000000023.000046040.html

(この記事は自分含めてアプリの開発をメインで行っていたエンジニア @yoshi-jr / @centerfield77 と共同で執筆しています)

この記事を読んでわかること

  • CANARY のアプリについて技術スタックの全体像・概要が理解できる

話すこと

  • CANARY のアプリのアーキテクチャ
  • CANARY で利用しているモジュールとその歴史

話さないこと

  • CANARY のアプリ視点以外でのアーキテクチャ
  • 各モジュールの細かい内容

では、見ていきましょう。

アーキテクチャ

まずは、CANARYのアプリについて全体感がわかるインフラ図となります。

矢印について、おおよその処理・データの流れと捉えてください。また、このアーキテクチャはアプリの視点でまとめているためバックエンド・インフラについて簡略化している点があります。

では、このアーキテクチャ図で登場する各モジュールについて説明します。

GitHub Actions

GitHub Actions は GitHub のリポジトリに紐づいて実行される CI/CD ツールです。

CI にて下記のようなフローを設定し運用しています。

  • formatter/linter/type check/test などのルールをひととおり設定してチェック
  • リリース用の PR に対しては、ビルド & E2E テスト & Slack へのリリース通知
  • PR をオープンしたとき・ラベルを張り替えたときをトリガーにして CI を実行する

以前は CI 周りの整備が弱く人力でカバーしている範囲が広いような状態でした。CI を整備した後は、テスト実行・コード品質の担保・リリース作業の大部分が自動化され、現在は開発者がより機能開発にフォーカスできるようになっています。

EAS

EAS(Expo Application Services)は、Expo フレームワークを使用して開発されたアプリケーションのビルド、デプロイ、およびスケーリングを簡単にするための一連のクラウドサービスです。

EAS には EAS BuildEAS SubmitEAS Update などビルド・デプロイ周りをクラウドで行える機能が存在します。

https://expo.dev/

課題背景

アプリ開発でストアに提出するためのビルド作業などは開発者のローカル環境で行っていましたが、環境による原因不明のエラーでビルドできない人がいたり、新規でジョインしたメンバーがすぐにリリースを行えない状態でした。

そのため、クラウド上でビルド〜アプリリリースまで行える EAS を導入しました。

現在の運用

開発確認時は、ブランチごとにビルドを作成して実機確認を行なっています。また、ブランチにコードプッシュするたび EAS Update を行うことで変更内容も高速に反映できるようになっています。

リリース時は、リリース用ビルドの作成とストアへの提出を EAS 上で実行しており、これにより人の手を介した作業を極力減らしたリリースフローができています。

結果として、ビルド・リリースなどを EAS で完結することでリリースフローが属人化せずに誰でも行える状態になっています。また、クラウド上で実行できるため CI でビルド・リリースまで行うことができ、開発者の動作確認時間の削減やリリース時間の削減もできました。

MagicPod

MagicPod はノーコードかつクラウド上でアプリの E2E テストを実行できるサービスです。

https://magicpod.com/corporate/

課題背景

以前は、リリースのたびに人を集めて、開発者用のビルドを配布し、機能を触ってバグがないか確認していました。そのため、毎回手動ですべて確認する必要があるためリリースに時間がかかるのと、アドホックなテストなので主要な機能や動線の確認もれが起きる可能性がありました。

そのため、ノーコードの E2E テストである MagicPod を導入しました。E2E のテストコードを自分たちで書く選択肢もありましたが、少人数のチームで開発を回す必要があったため運用コストがより少ないほうを選択したのが背景です。

実運用

MagicPod はテストケースを GUI で作成しビルドしたアプリをアップロードすることで、CI 経由で E2E テストが実行できるようになっています。CANARYアプリ ではリリース用の PR を作成し、その CI でアプリのビルドを Magic Pod にアップロードして E2E が行われています。

これにより、リリース前の E2E テストで「アプリが起動できているか」「主要な機能が動いているか」などの最低限の確認ができるようになりました。テストケースのメンテコストは必要ですが、今まで行っていた手動テストに比べれば確認の負担が大幅に削減できています。

Firebase

Firebase は、Google によって提供されているモバイルおよびウェブアプリケーション開発プラットフォームおよび開発キットです。

課題背景

プッシュ通知やダイナミックリンクなどの機能が必要になり、これらの機能を自前で作成するよりもひととおり揃っている Firebase を導入するほうが現実的だったため採用しました。

現在の運用

CANARYアプリ では、下記のように Firebase を利用しています。

  • プッシュ通知(Firebase Cloud Messaging)
    • ex) ユーザーが物件検索をした条件をもとに、毎日午後 8 時に新着物件を知らせる。
  • AB テスト(Firebase Remote Config)
    • ex) 新規機能について段階的リリース・AB テスト
  • ダイナミックリンク(Firebase Dynamic Links)
  • 認証(Firebase Authentication)
  • 分析(Firebase Analytics)

また、バックエンド側でも Firebase SDK を利用できるため、必要に応じて処理のコントロールをバックエンドで行っています。

Twilio

Twilio は、API を通じて SMS、音声・ビデオ通話機能を行えるサービスです。

課題背景

「電話で物件へ問い合わせ」を行いたいという要望が出てきたため、これを実現するために Twillio を入れることにしました。

実運用

Twilio では Programmable Voice という機能が提供されており、これを使ってアプリ上で Voice-over-IP (VoIP) 通話による電話機能を実現します。

https://www.twilio.com/ja-jp/voice/features

実装をした当初は React Native で利用できるライブラリがなかったため、 Native Module を自前で実装していましたが、 Native Module のメンテコストが高くまた属人化しており、かつ問題が発生したときの原因究明も難しい状態でした。

その後、しばらくしてから下記のライブラリが公開されるようになったため、こちらを利用することにしました。

https://github.com/twilio/twilio-voice-react-native

当初はまだプレビュー版だったこともあり、入れた後に原因不明の問題も起きやすかったですが、自前で実装したモジュールを利用し続けるメンテコスト・属人化の問題などに比べるとベターで、また今ではライブラリのバージョンアップが進んで品質も落ち着いてきたので安定して稼働している状態です。

Sentry

Sentry は、エラーの可視化・監視ツールです。

課題背景

以前はアプリで発生していた不具合について、アプリレビューや問い合わせメールなどでしか検知できない状況なうえに、ユーザーからのフィードバックだけでは詳細なエラー発生箇所・条件を知ることが困難でした。

そのため、エラーの調査や修正を迅速に行うために、エラー監視ツールとして Sentry を導入しました。

現在の運用

Sentry にはエラーのスタックトレースを記録する機能、ユーザーのタップやネットワークリクエストを記録するような機能が存在して、エラーがいつどこでどんなタイミングで発生しているのかがより詳細にわかるようになります。

Sentry にて新しいエラーが検知されたときに Slack にアラートを流し、アプリ開発をするエンジニアや運用担当になっている人がアラートを確認しトリアージをします。設定・運用についての詳細はこちらで解説しています。

https://zenn.dev/canary_techblog/articles/76711a9779bce7

Sentry 導入によってアラートを元にスタックトレースや操作の記録などエラーが発生した状況が細かくわかるようになり、エラーに対応するまでの時間短縮につながっています。また、エラーが発生した OS やユーザー数がわかることで問題がクリティカルなのかの判断も各メンバーで可能な状態になっています。


技術スタック

ここからは、CANARY のアプリで使用している言語・フレームワーク・ライブラリなどの技術スタックについてまとめていきます。

React Native / Expo

Expo は、React Native ベースのフレームワークでモバイルアプリ開発とデプロイメントを簡素化するツールセットです。

開発初期から React Native でアプリ開発をしており、必要に応じて Native の機能も拡張していました。現在は、Native に手を出すとメンテコストが上がってしまうためできる限り Expo の中で収めるようにしています。

TypeScript

TypeScript は JavaScript のスーパーセットな静的型付け言語です。

開発初期では JavaScript で実装していましたが、開発効率・型間違いによるエラーが多かったためすべてのコードを TypeScript にリプレイスしています。

Redux Toolkit / RTK Query

Redux Toolkit (RTK) は Redux の公式ツールキットです。

状態管理やデータフェッチングのためのユーティリティが提供されています。RTK Query は RTK の中の 1 つでデータフェッチに関わる状態管理をつかさどっています。

https://redux-toolkit.js.org/

開発初期は「素の Redux + 自前 Middleware」を利用しており、またディレクトリも feature ベースではなく function ベースで整理されていました。これらは Redux のベストプラクティスから逸脱している状態だったので、まずはベストプラクティクスに乗せるようにリファクタリングしました。

https://redux.js.org/style-guide/#structure-files-as-feature-folders-with-single-file-logic

他の選択肢として、Redux からの脱却もあり得るかと思いますが CANARY のアプリは 1 つのアプリ内で賃貸と売買の 2 つのサービスを提供しているため状態管理のライブラリ自体を他のものに置き換えるハードルが高く、まずはベストプラクティクスに乗せるほうを優先しました。

現在は、ほぼリプレイス・リファクタが終わっているため feature-base なディレクトリ + RTK Query で運用されています。

react-native-reanimated

react-native-reanimated は、React Native のコンポーネント要素などをアニメーションするために利用しているパッケージです。

https://github.com/software-mansion/react-native-reanimated

CANARY のアプリ では、このような細かいアニメーションで使われています。

Lottie

Lottie は、AirBnB が開発している JSON ベースのファイルを元にアニメーションを行う技術です。

https://airbnb.io/lottie/#/

CANARY のアプリでは、このようなアニメーションが必要な場所で使われています。

react-native-maps

react-native-mapsは、React Native アプリケーションで地図を表示および操作するためのパッケージです。

https://github.com/react-native-maps/react-native-maps

具体的な実装としてこちらで紹介しているとおり CANARY のアプリでは物件詳細ページやなぞって検索で使っています。

https://zenn.dev/canary_techblog/articles/1471c2f24ba659

Storybook

Storybook は、フロントエンドのコンポーネントを開発、ドキュメント化、テストするためのツールです。

以前は Storybook を作成、運用していたのですが下記のような理由でメンテをストップしました。

  • メンテコストが高い
  • カタログが壊れる
  • カタログを使っていないケースが多かった

現在は開発が落ち着いてかつバックエンドの開発をしていた人が合わせてアプリの機能を作成することも出てきているため、オンボーディングコストを下げるためにも導入を再開したいと考えています。

設計

ポイントとなる設計、思想などについてまとめていきます。

ディレクトリ

CANARY のアプリでは、基本的に feature-base でディレクトリが運用されていて各モジュールについては下記のような形で切り出すことが多いです。

/features/[feature]
├─ namespace.ts       # APIで使用する名前管理
├─ slice.ts           # RTK slice
├─ selector.ts        # Redux selector
├─ thunk.ts           # Redux Thunk
├─ api.ts             # 外部サービスへのAPI
├─ storage.ts         # ローカルストレージ
├─ hooks.ts           # カスタムフックス
├─ converter.ts       # データ変換
└─ tracker.ts         # イベントトラッキング

課題感としては、UI Component は feature-base のディレクトリとは別で管理しているので、可能ならこちらにまとめていきたいと考えています。

UI コンポーネント

CANARY のアプリでは、Atomic Design をベースに 4 層の独自レイヤーでコンポーネントを分けています。

Atomic Design では、粒度が小さい順にatoms / molecules / organisms / templates / pages という 5 層に分けて実装をします。詳細はこちらを参照してください。

https://atomicdesign.bradfrost.com/chapter-2/

CANARY のアプリでは、粒度が小さい順に basics / features / pages / screens という 4 層に分けて実装しています。

  • screens アプリのナビゲーション単位のコンポーネントが定義されたレイヤー
  • pages アプリの画面単位のコンポーネントが定義されたレイヤー
  • features ビジネスロジックを集めたレイヤー
  • basics UI に関するロジックや定義を集めたレイヤー

この UI コンポーネント設計の課題として、下記のようなものがあります。

  • screens はルーティングのためのレイヤーなのにビジネスロジックが書かれてしまっている
  • どこに何を置くべきかの基準となるルールが明確に決まっておらず個々人のレビューで判断される
  • コンポーネントの切り口を feature-base に変えたい

API

Schema 駆動で開発をしています。

API と疎通するための TypeScript のモジュールは下記のようなフローで自動生成しています。

  • 流れ
    1. バックエンド側のリポジトリにて、proto ファイルを更新し、その proto を元に OpenAPI のビルダーで swagger.json を更新します。
    2. swagger.json を元に、API 通信するための TypeScript のモジュールを生成してモバイル側のリポジトリに PR が自動で生成されます。

以前は、バックエンドの変更に対して手動で TypeScript のスタブも更新する必要がありましたがヒューマンエラーにより型違反・アプリのクラッシュなどが起きていました。現在では、Schema 駆動かつ自動生成によりこういったヒューマンエラーは発生しない状態になってます。

その他

linter に任せる

開発初期では linter が機能していないところから始まり、途中から 1 つずつ linter のルールを入れました。ルールを入れる際は「書き方に関するレビュー指摘があるたびに、対応するルールを調べて入れていく」ということを地道に行いました。

ただ、たとえばルールを 1 つ入れると既存コードの違反箇所でも弾かれるようになりすべてを変えていくのは大変だったため、途中からはルールを入れることを優先して違反となる既存コードについてはコメントで linter を skip することで、とりあえずルールを入れる対応を優先していました。

こういったルール導入を地道に 1 つずつ入れていった結果、現在はひととおりのルールが完備されている状態です。そのため、レビュワーへの依頼前に実装者が CI や自動修正により違反に気づいて修正が行えるようになっています。

おわりに

CANARY のアプリについての技術スタックをまとめた記事でした。

「より詳細に話を聞きたい」「自分もこの開発に興味がある」と感じて頂けましたら、カジュアル面談などでお気軽にコンタクトをとっていただければと思います!

Canary Tech Blog

Discussion