Save 37% off PRO during our Black Friday Sale! »

クライアントポータルを支える技術 / Technologies supporting VQ Client Portal

0a1c26fc11e4e93ec31df9776ae5f058?s=47 dkita
May 20, 2019

クライアントポータルを支える技術 / Technologies supporting VQ Client Portal

visasQ Engineering Meetup #2 -ビザスクを支える技術-
クラフトビールを片手にビザスクの話をする会
https://visasq.connpass.com/event/128398/

0a1c26fc11e4e93ec31df9776ae5f058?s=128

dkita

May 20, 2019
Tweet

Transcript

  1. クライアントポータルを支える技術

  2. 自己紹介 喜多 太樹 Daiki Kita 入社:2017年10月
 
 所属:VQポータル開発チーム
 
 生息地:東京都北区赤羽


  3. 今日お話すること • クライアントポータルの紹介 • クライアントポータルのチャットシステム
 • Firebase Realtime Database -

    設計のポイント
 • データ構造 / ルール
 • 今後

  4. クライアントポータルの紹介

  5. 2種類のスポットコンサル形式

  6. フルサポート形式 (VQ) クライアント リサーチ担当 案件担当 アドバイザー ⭕ ⭕ ⭕ ③リストアップ

    ②サーチ依頼 クライアント ポータル ⑤スポットコンサル ①案件依頼 ④面談調整 ④面談調整 依頼専用フォーム ビザスク
  7. 技術 App Engine Cloud SQL App Engine Cloud SQL クライアントポータル

    Cloud Functions Cloud Pub/Sub Cloud Scheduler ビザスク
  8. チャットを支える技術

  9. • クライアントとスタッフの1対1のチャット
 • 未読 / 既読表示
 • 未読件数表示
 • 未読通知メール送信


    • タイピング状態表示
 • オンライン状態表示 チャットの機能
  10. チャットの機能 クライアントポータル ビザスク

  11. チャットの構成 認証 メッセージ 通知API 通知API 同期API クライアントポータル ビザスク

  12. Firebaseを採用した理由 • 認証、データベースをはじめアプリケーション構築に必要な機能が一 通り揃っている
 • バックエンドの開発が不要、運用コストが少ない
 • 実装の自由度が高い
 • 情報量が豊富


  13. (当時は) App Engine Flexが WebSocketに対応していなかった バックエンドの構築と運用に 不安がある (時間もない) 日々変化するビジネス要件に 耐えうる気がしなかった

    Firebaseを採用した理由
  14. 利用しているプロダクト

  15. 利用しているプロダクト

  16. Firebase Realtime Database • NoSQL
 • データがリアルタイムに同期される
 • オフライン対応
 write

    sync sync sync
  17. 設計の悩みどころ • スキーマレスな故に自由度が高い
 • 正規化しすぎるとクエリができない
 • アクセス制御のためにルールを書く必要がある
 • データに型がないのでルールで担保する


  18. クエリしやすく
 設計のポイント①


  19. クエリ - 並べ替えとフィルタ • orderByKey()
 • orderByChild()
 • orderByValue()
 •

    limitToFirst()
 • limitToLast()
 • startAt()
 • endAt()
 • equalTo()
 × ❌ 複数条件での並べ替えやフィルタはできない

  20. データを冗長化する① { "rooms": { "roomA": { "users": { "0001": true

    } }, "roomB": { "users": { "0001": true } } }, "users": { "0001": { "name": "userA" } } } // rooms をすべて取得する const snapshot = await db.child(‘rooms’).once('value'); // roomsの件数文のループが発生する snapshot.forEach(async child => { const room = child.val(); if (‘0001’ in room.users) { // OK } }); ❌ rooms の件数に応じて処理数が膨れ上がる
 例) ユーザーが所属しているチャットルームを取得する
 

  21. データを冗長化する① { "userRooms": { "0001": { "rooms": { "roomA": true

    "roomB": true } } } } // rooms をすべて取得する const snapshot = await db.child(‘userRooms/0001/rooms’).once('value'); // OK const rooms = child.val(); ⭕ rooms の件数に依存しない
 例) ユーザーが所属しているチャットルームを取得する

  22. データを冗長化する②
 { “rooms”: { “room1”: { } }, “messages”: {

    “message1”: { “text”: “こんにちは” } }, "unreadMessages": { "room1": { "messages": { “message1”: true }, “count”: 1 } } } // 未読のある部屋を取得する const snapshot = await db.child('unreadMessages') .orderByChild('count') .startAt(1) .once('value'); ⭕ count 済みのデータも保存しておく
 例) 未読のあるチャットルームを取得する 

  23. データ設計のポイント • データをネストしない • データ構造を平坦化する • スケーラブルなデータを作成する https://firebase.google.com/docs/database/web/structure-data?hl=ja

  24. ルールによるアクセス制御
 設計のポイント②


  25. Realtime Database ルール • データベースへの読み書きのアクセス制御
 • フィールドのバリデーション
 • インデックス


  26. ルールによるアクセス制御
 { "users": { "0001": { "name": "client" }, "0002":

    { "name": "staff" } } } 例) 自分のデータのみ読み込みを許可 
 { "rules": { "users": { "$uid": { ".read": "(auth != null && auth.uid == $uid)" } } } } ルール Who are you ? I’m ‘client’. I’m ‘staff’. ❌ 
 認証済み ユーザーIDが一致する
  27. ルールによるアクセス制御
 { "users": { "0001": { "name": "userA" }, "0002":

    { "name": "userB" } }, "userFollowers": { "0001": { "0002": true }, "0002": { "0001": true } } } 例) 他人のデータも読み込みを許可 
 { "rules": { "users": { "$uid": { ".read": "((auth != null && auth.uid == $uid) || root.child('userFollowers').child(auth.uid).child($uid).val( ) != null)" } } } } ルール I’m ‘client’. I’m ‘staff’. ⭕ 

  28. 実際のルール !? 


  29. Bolt Compiler • JavaScript 風 • データモデル定義
 • バリデーション定義
 •

    ルールの関数化

  30. Type, Validation, Function ...

  31. 自分のデータの読み込みを許可する { "rules": { "users": { "$uid": { ".read": "(auth

    != null && auth.uid == $uid)" } } } } isSignedIn() { auth != null } isCurrentUser(uid) { isSignedIn() && auth.uid == uid } path /users { read() { isCurrentUser(auth.uid) } write() { false } }
  32. 実際のルール !? 


  33. ルール設計のポイント • アクセス制御のためのデータを定義する • Bolt Compilerを使う • ルールのテストを書く

  34. 設計のポイント まとめ • 取り扱いやすいデータ構造にする
 • クエリのしやすさを重視する
 • ときには冗長化する (Cloud Functions

    に委譲する)
 • アクセス制御をしっかりと
 • 制御用のデータを定義する
 • Bolt Compiler を使う

  35. 今後 • 手動オペレーションの削減 • データ分析
 • Realtime Database -> Cloud

    Firestore へ • Cloud Functions を Node.js -> Go へ
 • ....
 MUST WANT
  36. ありがとうございました