こちらは ABEJA アドベントカレンダー2021の 21 日目の記事です。
はじめに
こんにちは。CS 統括部システム開発グループ 1 の石川 (@ishikawa) です。緊急事態宣言の解除を受け、ABEJA でも各自任意での出社が可能となりました 2。今回は ABEJA のサテライトオフィスである「Bizflex 麻布十番」を紹介するとともに、オフィスでの新しい働き方をサポートするために私たちが開発しているアプリケーション及びシステムのアーキテクチャについて書きたいと思います3。
この記事ではフロントエンド中心の話になります。バックエンド側についてはオフィスDXを支える技術(バックエンド編)をご覧ください。
Bizflex の紹介
オフィスの様子
まずはこちらの写真をご覧ください。「Bizflex 麻布十番」の 1F、共有部の様子です。
Bizflex はフロア占有型のセットアップオフィスとなっています。そのため、専有部では他社の方を気にすることがなく、会議や開発に集中することが可能です。また、Wi-Fi 含めたインターネット環境も完備のため、入居したその日から働くことができます。写真は入居前のものですが、デスクや椅子も揃っていることが分かるかと思います。余談ですが、この椅子はかなり座り心地がいいです。
ABEJA が開発するアプリケーション
共有部および専有部の入口ドア横には受付端末として iPad が設置されています。入居者以外のゲストの方は、QR コードによるドアの解錠(共有部の場合)、担当者の呼び出し(専有部の場合)などが可能です。4 この端末で動作する iOS アプリケーション(以下「受付アプリ」)は ABEJA が開発しています。
テナント入居者の方向けには iOS/Android のアプリ(以下「Bizflex アプリ」)が用意されています。このアプリにはテナント企業で契約されている Google Workspace / Microsoft 365 アカウントでログインできます。5
ホーム画面からは共有会議室の予約・空き状況やゲストの来館通知以外にも、同僚のステータスを一覧できる画面が用意されています。Bizflex システムは館内ドアのセキュリティ、オフィスの Wi-Fi ルータとシステム連携しており、また、Google Workspace / Microsoft 365 カレンダーや Slack / Teams などのコミュニケーションツールとの連携も設定可能なため、
- 現在、オフィスに出社している同僚と、活動しているフロアの表示
- カレンダー上で予定が埋まっているかどうか
- Slack / Teams などのコミュニケーションツールでの会話
などができるようになっています。
共有会議室の予約はアプリからも可能ですが、Google Workspace / Microsoft 365 カレンダーと密に連携しているため、カレンダーからいつも通り共有会議室を予約するだけで自動的に予約されるようになっています。
最後に、テナント企業の管理者向けには管理ツール(以下「管理ツール」)が提供されています。この管理ツールでは、
- ユーザー(従業員)の管理
- 従業員およびゲストの入室ログ確認
- 会議室予約に必要なポイントの利用ログ確認
などができるようになっています。
合計で 3 つのフロントエンド・アプリケーション(「Bizflex アプリ」「受付アプリ」「管理ツール」)を ABEJA が開発・運用しています(内部向けには他のツールもありますが、今回は割愛)。
アーキテクチャ
ここからは上記のフロントエンドを構成するテクノロジースタックやアーキテクチャの概要を紹介します。前節で紹介した通り、Bizflex のフロントエンドには 3 つのアプリケーションがあり、それぞれの対応プラットフォームは以下の通りです。
アプリケーション | 対応プラットフォーム |
---|---|
Bizflex アプリ | iOS/Android 1 |
受付アプリ | iOS |
管理ツール | Web |
Bizflex アプリ・受付アプリはネイティブ・アプリケーション、管理ツールは Web アプリケーションですが、これらのフロントエンドはすべて TypeScript と React で構築しています。
フロントエンドで採用しているフレームワークと採用理由
ネイティブ・アプリケーション開発には Expo という開発フレームワーク/プラットフォームを利用しています。Expo は React Native を基幹技術としつつ、開発に必要なツールやライブラリを整備することで非常に心地よい開発体験を提供してくれます(具体的な例は後述します)。
ネイティブ・アプリケーション開発の技術選定を行ったのは 2020 年の 12 月でした。過去の議事録を読み返してみると、選定時には以下のことが重要だったようです。
- 開発メンバーが少なく、ふたつのプラットフォームを個別に開発するのは厳しい
- 社内に iOS/Android 開発の経験者がほぼおらず、ゼロから Swift/Kotlin を習得して開発するのはリスクが高い
- 新しい開発メンバーの獲得がしやすいこと
候補としては Flutter も挙がっていましたが、上記の理由から、慣れ親しんだ TypeScript と React が使える Expo (React Native) の調査をすることになりました。その後、数ヶ月のプロトタイプ開発を経て、「今回の要件であれば十分いける」という感触を得たため、正式採用となった次第です(結果的には非常に満足しています)。6
管理ツールの Web アプリケーション開発には Next.js を採用しています。こちらは社内でも採用事例が増えていたこともあり、採用に大きな懸念はありませんでした。もっとも、SSR も SSG も使っておらず、単純な SPA なので、Next.js の技術的なアドバンテージを活かしきっているとは言えません。むしろ、TypeScript や Routing の設定の容易さや、社内含めたコミュニティの成長を見て採用したところが大きいです。
共通するライブラリやツール
すべてのフロントエンドを TypeScript と React に統一できたことで、ツールやライブラリで同じものを使えるようになりました。いくつかの実例を紹介します。
- 日付操作ライブラリ Day.js
- 効率的なデータ取得のためのライブラリ SWR
- フォームバリデーションライブラリ React Hook Form
- 多言語対応 react-i18next documentation
- テスティングフレームワーク Jest と React Testing Library。API のモックには Mock Service Worker
- コードフォーマッタ Prettier
- リンター ESLint
ライブラリ以外にも、原則として Functional Component で開発し、ロジックや共通処理はカスタムの Hooks に隠蔽する開発方針や、GitHub Actions による自動 CI/CD も共通しているところです。
一例として、React Hook Form を利用しているコードを、Bizflex アプリと管理ツールから抜粋してみましょう(読みやすさのために簡略化しています)。
Bizflex アプリで React Hook Form を利用しているコード例
import { Controller, useForm } from 'react-hook-form'; import { StackScreenProps } from '@react-navigation/stack'; import { AccountStackParamList } from 'Routes'; export const AccountProfileScreen: React.VFC< StackScreenProps<AccountStackParamList, 'AccountProfile'> > = () => { const { control, handleSubmit, formState: { errors, isDirty, isSubmitting }, } = useForm<EditProfileForm>({ mode: 'all' }); const { data: me, mutate: mutateMe } = useMe(); const invokeUpdateMe = useInvokeUpdateMe(); const onSubmit = handleSubmit(async (formData) => { const response = await invokeUpdateMe({ name: formData.displayName }); mutateMe && mutateMe(response); }); ...
管理ツールで React Hook Form を利用しているコード例
import { useForm } from 'react-hook-form'; type Props = {...}; const CreateRoomForm = ({ room, buildings, onCreateRoom }: Props) => { const { handleSubmit, control, formState: { errors }, } = useForm(); const onSubmit = async (value) => { const selectedFloor = JSON.parse(value.floor); await onCreateRoom({ buildingId: selectedFloor.buildingId, floorName: selectedFloor.floor, name: value.roomName, meetingRoomId: room.id, }); };
それぞれ違う開発者が書いたコードで、微妙に異なるものもだいたい同じようなコードになっているのが分かるかと思います。もし、仮に、どちらかのアプリケーションが Formik を採用していたら、開発者は React Hook Form に加えて Formik の API を習得する必要があり、かなりツラいことになっていたでしょう。
共通のエコシステム、開発プラクティスを使えることで、別々のフロントエンドを開発するときも開発者のコンテキスト・スイッチ負荷が少なく、生産性向上に寄与しています。もちろん、採用時から狙っていたことではありますが、想定以上の効果を実感しています。
実際の開発体験
最後に、Expo (React Native) での開発がどのようなものなのか、実際のコードベースを例に紹介して終わりたいと思います。
まず、GIt リポジトリを clone したら、npm
で依存ライブラリをダウンロードします。
$ npm install
初回は、Expo のコマンドラインツールもインストールしておきます。
$ npm install --global expo-cli eas-cli
npm run start
すると、Expo のコマンドラインツールを使って開発サーバが起動します。
$ npm run start > start > expo start Starting project at /home/ishikawa/bizflex-client Developer tools running on http://localhost:19002 Opening developer tools in the browser... Starting Metro Bundler ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ █ ▄▄▄▄▄ █▄▄▄ ▀▄██▄█ ▄▄▄▄▄ █ █ █ █ ██▄▀ █ ▀▀▀█ █ █ █ █ █▄▄▄█ ██▀▄ ▄ ████ █▄▄▄█ █ █▄▄▄▄▄▄▄█ ▀▄█ ▀ █▄█▄▄▄▄▄▄▄█ █ ▄▀ █▄▀█▄▀█▄██ ▀█▄█▀█▀▀▄█ ██▀▀▄█ ▄ ▀▄██▄█ ▄ ▀███▄▀▀ █ █ █▀▀█▄▄▀▀▄ █▀█▄ █ ▄▀▀█▀ ██ █ ▄▄█ █▄ ▄▀▀█▀▄█ ▄▀ ██▄▀ █ █▄█▄▄▄▄▄▄ █▀ ▄▄ █ ▄▄▄ ▄▀▄█ █ ▄▄▄▄▄ ██▀▀▀▄ █ █▄█ ███ █ █ █ █ █ ▀▄ ██▄ ▄ ▄ █▀▀█ █ █▄▄▄█ █▀▀▄ █▄ ▄█▀▀▄█ █ █▄▄▄▄▄▄▄█▄▄▄▄██▄▄▄▄█▄▄███▄█ › Metro waiting on exp://192.168.11.6:19000 › Scan the QR code above with Expo Go (Android) or the Camera app (iOS) › Press a │ open Android › Press i │ open iOS simulator › Press w │ open web › Press r │ reload app › Press m │ toggle menu › Press d │ show developer tools › shift+d │ toggle auto opening developer tools on startup (enabled) › Press ? │ show all commands Logs for your project will appear below. Press Ctrl+C to exit.
同時に、Web ブラウザが起動し、以下のような画面が表示されます。ここからシミュレーターを起動してアプリケーションを動作確認したり、QR コードを読ませることで実機での確認が可能です。
また、接続を Tunnel モードにすると、自動的に ngrok が(なければインストールされて)起動し、トンネルサーバー経由でアクセスすることが可能になります。これは実機での確認に非常に便利で、重宝しています。このように、開発者にとって「痒いところに手が届く」機能が充実しているのが、Expo のいいところです。
開発中のアプリケーションは、Expo Go というアプリ内で起動します。開発中にコードを変更すると、アプリケーション側にも自動的に変更が反映されます。毎回ビルドや起動をやり直す必要はありません。Web 開発では当然となっているホットリロードですが、ネイティブ開発でも同様の体験ができ、反映も非常に速いです。
機能開発や修正は GitHub の Pull Request にします。そうすると、GitHub Actions でリンターやテストが自動実行されます。そして、Pull Request がマージされると、Expo の EAS Build サービスでアプリケーションがデバイス向けにビルドされ、発行されたリンクからデバイスにインストールすることができるようになります(Slack に通知するようにしています)。
以上が開発のおおまかな流れになります。ネイティブ・アプリケーションであっても Web 開発とほぼ同じ開発フローで開発できますし、何より、Xcode や Android Studio を起動する必要がないのは最高です🙂
また、最近の Expo は SDK 43, 44 が立て続けにリリースされ活発に開発されています。今まで、Expo で用意されている以外のネイティブ機能を使いたい場合は eject して React Native によるコードベースを直接編集するしかありませんでしたが、EAS Build によって eject することなくネイティブ・ライブラリを組み込めるようになりました(開発ビルド向けに独自の Expo Go クライアントをビルドすることもできます!)。
まとめ
今回は概要の紹介のみだったため、技術的に面白い話はあまり出来ませんでしたが、いかがでしたか? サービスの内容や開発の様子が少しでも伝われば幸いです。
そして、Bizflex 開発チームでは一緒にサービスを作ってくれるエンジニアを募集中です!「まずは話を聞いてみたい」という方にカジュアル面談もお待ちしておりますし、事前コーディング・テストの内容も GitHub で公開 (TypeScript / Go) していますので、応募には興味のない方もぜひご覧ください。
-
今後、Web にも対応予定↩
-
ABEJA はヒューリック株式会社と資本業務提携契約を締結し、事業戦略パートナーとして、「Bizflex by HULIC」シリーズに導入されているシステムの開発を進めています。参考: ABEJA、ヒューリック株式会社と資本業務提携契約を締結 | 株式会社ABEJA↩
-
テナント入居者の方はカードキーでも解錠できます。↩
-
外部 IdP (Google Workspace / Microsoft 365) と連携設定せずに、メールアドレスのみでのアカウント発行も可能です。↩
-
私自身、Objective-C や Swift によるネイティブ・アプリケーション開発経験が長かったこともあり、クロスプラットフォーム・フレームワークには懐疑的な面もありました。↩