Slide 1

Slide 1 text

freee 株式会社
 freee Slide Format RailsとFirebaseで作るリアルタイムチャット


Slide 2

Slide 2 text

● freee株式会社所属
 ● Engineer
 ● 開発効率高めるのが好き
 ● 趣味はrubocopの適用
 山崎遼介 moai
 Engineer
 2

Slide 3

Slide 3 text

3 今日のゴール
 RailsとFirebaseで作るリアルタイムチャットの仕組みを紹介


Slide 4

Slide 4 text

4 前提
 作りたい機能、技術stack


Slide 5

Slide 5 text

5 プロジェクト管理freee
 ● プロジェクトにまつわる業務を楽にしたい


Slide 6

Slide 6 text

6 作るモチベーション
 ● プロジェクトに関する情報を雑に記録する
 ● 経営者 <-> プロジェクトマネージャー 間で気になったことを聞ける


Slide 7

Slide 7 text

7 ● 


Slide 8

Slide 8 text

8 技術スタック:前提
 ● Ruby on Rails
 ● OpenApi
 ● TypeScript
 ● React


Slide 9

Slide 9 text

9 課題


Slide 10

Slide 10 text

10 リアルタイムのチャットをどう作るか?(ざっくり)
 ● つまりはポーリングどうするの?
 ○ ロングポーリング? ○ 世の中に有るイベントサブスクライブサービス使う? ○ Firebase?(NEW!)

Slide 11

Slide 11 text

11 Firebase使う懸念は?
 ● 個人情報はどこに置くのか?
 ○ Firebaseに個人情報置く? ○ 置くとするとセキュリティはどう担保する? ● Railsとの連携はどうするのか?
 ○ RailsとID連携? 


Slide 12

Slide 12 text

12 Firebase使う懸念は?
 ● 個人情報はどこに置くのか?
 ○ Firebaseに個人情報置く? ○ 置くとするとセキュリティはどう担保する? ● Railsとの連携はどうするのか?
 ○ RailsとID連携?

Slide 13

Slide 13 text

13 どうなったの?


Slide 14

Slide 14 text

14 アーキテクチャざっくり
 Data Fetch 
 個人情報のやり取り
 Event Publish
 イベントが
 起こったことだけ
 を通知
 Event Subscribe
 イベントが
 起きたことだけを受 信
 新しい Event 有る?


Slide 15

Slide 15 text

15 Rails側擬似コード
 class TimeLineMessage < ApplicationRecord after_create_commit -> { Gcp::Firestore.client.col('timeline_notifications').doc.set( identifier: "#{id}-timeline", created_at: Time.current ) } end


Slide 16

Slide 16 text

16 firebaseのデータ


Slide 17

Slide 17 text

17 JavaScript側擬似コード
 export const ChatContainer = (id) => { 2 const [timelines, setTimeLines] = React.useState([]); const [latestId, setLatestId] = React.useState(0); 3 const reloadTimeline = (id) => { Api.fetchTimeline(setTimeLines); setLatestId(id);}; 4 const usePushNotification(reloadTimeline) = firestore 5 .collection('timeline_notifications').where('identifier', '==', "${id}-timeline") 6 .limit(1).nSnapshot(querySnapshot => { 7 if( querySnapshot.docs[0].id !== latestId ) reloadTimeline(querySnapshot.docs[0].id); 8 }); 9 React.useEffect(() => { usePushNotification(reloadTimeline) } ); 10 return (); }

Slide 18

Slide 18 text

18 良かったこと


Slide 19

Slide 19 text

19 サブスクライブ処理はfirebaseにおまかせできた
 新しい Event 有る?
 ここはFirebase Client がやってくれる Data Fetch 
 個人情報のやり取り
 Event Subscribe
 イベントが
 起きたことだけを受 信
 Event Publish
 イベントが
 起こったことだけ
 を通知


Slide 20

Slide 20 text

20 フロント側の処理はここだけ
 export const ChatContainer = (id) => { 2 const [timelines, setTimeLines] = React.useState([]); const [latestId, setLatestId] = React.useState(0); 3 const reloadTimeline = (id) => { Api.fetchTimeline(setTimeLines); setLatestId(id);}; 4 const usePushNotification(reloadTimeline) = firestore 5 .collection('timeline_notifications').where('identifier', '==', "${id}-timeline") 6 .limit(1).nSnapshot(querySnapshot => { 7 if( querySnapshot.docs[0].id !== latestId ) reloadTimeline(querySnapshot.docs[0].id); 8 }); 9 React.useEffect(() => { usePushNotification(reloadTimeline) } ); 10 return (); }

Slide 21

Slide 21 text

21 Firebaseには顧客情報置かなくて済んだ
 class TimeLineMessage after_create_commit -> { Gcp::Firestore.client.col('timeline_notifications').doc.set( identifier: "#{id}-timeline", created_at: Time.current ) } end
 通知イベントのみFirebaseに連携

Slide 22

Slide 22 text

22 じつは


Slide 23

Slide 23 text

23 社内で先行事例が
 ● Takumi Ohashi@_tohashi による
 Firebaseを使ってチャット機能を作る先行事例があった
 


Slide 24

Slide 24 text

24 まとめ


Slide 25

Slide 25 text

25 まとめ
 ● FirebaseをEvent通知だけに限定して使うのはめちゃ良い
 ● Railsと一緒に使う場合もそんなに悪くない。
 ○ ただ、ログイン連携は闇の気配。 


Slide 26

Slide 26 text

Firebaseはいいね


Slide 27

Slide 27 text

27 EOF