この記事はエイチーム引越し侍 / エイチームコネクトの社員による、Ateam Hikkoshi samurai Inc.× Ateam Connect Inc. Advent Calendar 2021 2日目の記事です。
「引越し侍 ネット見積もりサービス」
のβ版をリリースしました
昨日、 @sho-hata が
「引越し業界初の新サービスを支えるバックエンド技術」というのを投稿してくれたので、
であれば、フロントエンドも、、ということで @anneauがフロントについてお話させていただきます。
結構、チャレンジングな技術構成してるので、皆さんの参考になる部分もあるかと思い、
イケてるところとイケてないと思ってるところを書こうと思います。
技術構成
まず、全体の技術構成は図を見ていただければと思います。
画像作成: @sho-hata
フロントエンドに関係のある部分のみ解説します
Next.js
フロントエンドの構築にはNext.jsを採用しました。
Next.jsの詳細は不要かと思いますので、内部構成のみお伝えします。
- SSRで実行
- データの取得はGraphQL
- GraphQLクライアントにはReact Relayを採用
- ユーザー認証にはFirebase Authentication
- リアルタイム性が問われるチャットにはFirestore
Hasura
あまり聞き慣れないかと思いますが、HasuraはGraphQL エンジンという役割を担っております。
図のように、クライアントでGraphQLのクエリを定義し、Hasuraにリクエストを飛ばせば、自動的にSQLを作成し、データベースにクエリを走らせてくれます。
Firebase
ユーザー認証やチャットなどリアルタイム性が問われるところには、Firebaseを用いています。
Hasuraとも相性がよく、jwtでFirebase側のユーザー認証情報をHasura側で解釈することができます
イケてるところ
Next.js 採用による色々
普段開発してるとその恩恵が当たり前になりがちですが、
- DX向上
- Fast Refresh
- TypeScript Support
- UX向上
- Image Optimization
- その他
- SSR Support
- API Routes
等々、Vercel teamの皆さん、本当にお世話になっています
Hasura採用によるバックエンド工数削減
Next.js → Hasuraの通信にはGraphQLを用いています。
GraphQLでは、クライアントが望むデータをクライアント側に定義していくようになります。
query {
profile {
id
name
avatar
}
}
本来であれば、GraphQLサーバーの方で、このクエリを解釈し、SQLを発行する必要がありますが、
上で述べたとおり、これらは全てHasuraが担ってくれます。
認証やパフォーマンスチューニングもHasuraがやってくれるため、基本的にバックエンドが不要となります。
GraphQLでは解決できないような複雑な処理は
HasuraからRest APIを呼び出せるようになっています。
僕たちのサービスでは、暗号化を必要とする部分や複雑なビジネスロジックはGolangに任せています
React Relay × Hasura 採用による型の安全性
API側のスキーマとTypeScriptの型定義をあわせるのは労力のいることかと思いますが、
現状、ほぼ0工数で実現することができています。
GraphQLのクライアントには、React Relayを用いており、
Hasuraが出力してくれるGraphQLスキーマをRelay Compilerにかませることで、
バックエンドとフロントエンドの型に一貫性をもたせることができています。
Nx採用によるモノレポ化
Nxというのはフロントエンド特化のモノレポ管理ツールです。
モノレポとは?
複数のプロジェクトを一つのリポジトリで管理しようという概念で
GoogleやFacebook、Uberなど多くの企業が採用しているアーキテクチャです。
- リソースの共通化
- linterやbuilderの共通化
- コンポーネントや処理の共通化
- ビルドスピード向上
- 変更を加えていない部分はキャッシュすることでビルドスピードを上げる
- 依存関係の見える化
などメリットが多くあります。
例) ユーザー、提携企業、社内を一つのリポジトリで管理
このように一つのリポジトリで構成することでリソースの共通化がしやすくなります。
イケてないと思ってるところ
GraphQLによるフロントエンドのコード肥大化
GraphQLはクライアントから自由にデータを取り出せるというメリットがある一方、
パース処理などをクライアント側でする必要があります。
例) TODOコンポーネント
const STATUS = {
1: '未着手',
2: '着手中',
3: '完了',
};
const query = graphql`
query todoQuery on Todo {
title
status
deadline
}
`
const Todo: VFC = () => {
const data = useQuery(query);
return (
<>
<p>{data.title}</p>
<p>{STATUS[data.status]}</p>
<p>{dayjs(data.deadline).format('YYYY-MM-DD')}</p>
</>
);
}
バックエンドを介していれば、
- ステータスはバックエンド側で変換して、表示用に日本語をもらう
- 日付はフォーマットされたものを返してもらう
ということができますが、現状、これらがフロントの責務となってしまっています。
Firebaseの処理をクライアントが担っていること
構成図を見てもらえればわかるかと思いますが、
現状、フロントから発生する通信がHasuraに一本化できておらず、Firebaseとも通信をしております
できる限り保守しやすい状態は保とうとしておりますが、フロントエンドが担っている責務が大きすぎるような気がしています。
今後の展望
やりたいことは色々ありますが、まずはイケてないと思っているところで取り上げた、
「フロントエンドの肥大化」というところは解消していきたいと思っています。
世の中的にはBFFによる解決策が多いかと思いますが、
僕たちの場合、HasuraがBFFに近い動きをしてくれていますし、Nextjsのapi routesも一部使用しているため、少し冗長な気もしています。
引き続き議論は進めていきますが、少なくともFirebaseの処理はサーバーサイドに寄せていきたいと思っています。
明日
Ateam Hikkoshi samurai Inc.× Ateam Connect Inc. Advent Calendar 2021 2日目の記事は、いかがでしたでしょうか。
明日は @ex_SOUL の投稿になります!お楽しみに!