$30 off During Our Annual Pro Sale. View Details »

テーブル分割で実現するdeviseの責務の分離と柔軟な削除機構

なおと
October 30, 2023

 テーブル分割で実現するdeviseの責務の分離と柔軟な削除機構

なおと

October 30, 2023
Tweet

More Decks by なおと

Other Decks in Programming

Transcript

  1. テーブル分割で実現する
    Deviseの責務の分離と柔軟な削除機構
    Kaigi on Rails 2023
    なおと
    2023/10/28

    View Slide

  2. ⾃⼰紹介
    • サーバーサイドエンジニア
    • Doorkeeperが好き
    • エンジニア2年⽣、初登壇
    @NaotoCoding
    なおと

    View Slide

  3. ⽬次
    1. Deviseにおけるusersテーブルの分割
    a. Deviseとは
    b. Deviseをこんな⾵に使いたいな
    c. テーブル分割⽅法の提案
    d. テーブルを分割して実装してみた感想
    2. テーブル分割による柔軟な削除機構
    a. ユーザーの削除時に考慮したいこと
    b. 柔軟な削除機構を実現するための分割⽅法
    c. テーブル分割による削除機構の使⽤⽅法
    3. まとめ

    View Slide

  4. その前に。
    Deviseにおけるusersテーブルの分割については、
    jokerさんのブログ記事
    「パーフェクトRails著者が解説するdeviseの現代的なユーザー
    認証のモデル構成について」
    を参照し、⾃分の中で噛み砕きながらDeviseのテーブル構造に
    ついて考えました。
    https://joker1007.hatenablog.com/entry/2020/08/17/141621

    View Slide

  5. 1. Deviseにおけるusersテーブルの分割

    View Slide

  6. ⽬次
    1. Deviseにおけるusersテーブルの分割
    a. Deviseとは
    b. Deviseをこんな⾵に使いたいな
    c. テーブル分割⽅法の提案
    d. テーブルを分割して実装してみた感想
    2. テーブル分割による柔軟な削除機構
    a. ユーザーの削除時に考慮したいこと
    b. 柔軟な削除機構を実現するための分割⽅法
    c. テーブル分割による削除機構の使⽤⽅法
    3. まとめ

    View Slide

  7. • 認証機構を簡単に作成可能にするgem
    • サインイン
    • サインアップ
    • サインアウト
    • ユーザーの記憶
    • gem Wardenがベース
    a. Deviseとは
    引⽤: https://github.com/heartcombo/devise

    View Slide

  8. • 様々なオプションが提供されている
    • メール確認機能
    • ログイン複数回失敗時にロック
    • オプションもとても簡単に有効化可能
    • テーブルにカラムを追加
    • モデルクラスでモジュールを使⽤するだけ
    引⽤: https://github.com/heartcombo/devise
    a. Deviseとは

    View Slide

  9. とても便利で超有名なgem
    新規アプリ開発時には毎回お世話になっている
    a. Deviseとは

    View Slide

  10. ⽬次
    1. Deviseにおけるusersテーブルの分割
    a. Deviseとは
    b. Deviseをこんな⾵に使いたいな
    c. テーブル分割⽅法の提案
    d. テーブルを分割して実装してみた感想
    2. テーブル分割による柔軟な削除機構
    a. ユーザーの削除時に考慮したいこと
    b. 柔軟な削除機構を実現するための分割⽅法
    c. テーブル分割による削除機構の使⽤⽅法
    3. まとめ

    View Slide

  11. b. Deviseをこんな⾵に使いたいな
    1. 1つのモデルクラスが1つの責務を担当するようにしたい
    2. 雑な独⾃実装で不要な情報露出を⾏わせない

    View Slide

  12. Deviseはさまざまなオプション機能を提供している
    • メール確認
    • ログイン複数回失敗時ににロック
    1. 1つのモデルクラスが⼀つの責務を担当するようにしたい
    b. Deviseをこんな⾵に使いたいな

    View Slide

  13. Deviseはさまざまなオプション機能を提供している
    → 機能を追加するためにUserクラスでモジュールを読み込む
    1. 1つのモデルクラスが⼀つの責務を担当するようにしたい
    b. Deviseをこんな⾵に使いたいな

    View Slide

  14. Deviseはさまざまなオプション機能を提供している
    → 機能を追加するためにUserクラスでモジュールを読み込む
    → Userクラスが提供する機能(責務)が多くなる
    1. 1つのモデルクラスが⼀つの責務を担当するようにしたい
    b. Deviseをこんな⾵に使いたいな

    View Slide

  15. 認証⽤テーブル、クラスを使って責務を分担させたいな、、、
    1. 1つのモデルクラスが⼀つの責務を担当するようにしたい
    b. Deviseをこんな⾵に使いたいな

    View Slide

  16. オプション機能を追加するとカラムがusersテーブルに付与される
    2. 雑な独⾃実装で不要な情報露出を⾏わせない
    b. Deviseをこんな⾵に使いたいな

    View Slide

  17. 認証関連情報がusersテーブルに集約されているため
    Deviseが提供していない認証系機能を追加する際に
    usersテーブルにカラムを追加してしまう
    → 不要な情報の露出が⾏われるかも
    2. 雑な独⾃実装で不要な情報露出を⾏わせない
    b. Deviseをこんな⾵に使いたいな

    View Slide

  18. 例 : ソーシャルログインの追加
    2. 雑な独⾃実装で不要な情報露出を⾏わせない
    b. Deviseをこんな⾵に使いたいな

    View Slide

  19. usersテーブルに以下のカラムを追加する⽅法がよく⾒られる
    純粋なユーザー情報ではなく、認証⽤の情報であるため
    本来別のテーブルに配置するべき情報だと考えている
    • provider:認証プロバイダーの名称
    • uid:プロバイダーから受け取るuid
    2. 雑な独⾃実装で不要な情報露出を⾏わせない
    b. Deviseをこんな⾵に使いたいな

    View Slide

  20. 雑にレコードをrenderした時uidやproviderまで送信してしまう
    (これは明らかなヒューマンエラーである)
    2. 雑な独⾃実装で不要な情報露出を⾏わせない
    b. Deviseをこんな⾵に使いたいな

    View Slide

  21. 注意
    あくまで不要な情報露出は
    標準以外の認証系機能を追加した際に発⽣し得る懸念
    Deviseが標準で⽤意している追加機能においては
    この点のセキュリティは担保されている
    (confirmed_atやencrypted_passwordなど)
    2. 雑な独⾃実装で不要な情報露出を⾏わせない
    b. Deviseをこんな⾵に使いたいな

    View Slide

  22. 不要な情報の外部送信はできる限り避けたいな、、、
    usersテーブルは純粋なユーザーの情報だけ置きたいな
    2. 雑な独⾃実装で不要な情報露出を⾏わせない
    b. Deviseをこんな⾵に使いたいな

    View Slide

  23. 実際にテーブル分割を⾏ってみた

    View Slide

  24. ⽬次
    1. Deviseにおけるusersテーブルの分割
    a. Deviseとは
    b. Deviseをこんな⾵に使いたいな
    c. テーブル分割⽅法の提案
    d. テーブルを分割して実装してみた感想
    2. テーブル分割による柔軟な削除機構
    a. ユーザーの削除時に考慮したいこと
    b. 柔軟な削除機構を実現するための分割⽅法
    c. テーブル分割による削除機構の使⽤⽅法
    3. まとめ

    View Slide

  25. c. テーブル分割⽅法の提案
    テーブル分割を⾏う⼿法での⼀例として、以下の⼿順を提案する
    1. 機能に必要なカラムのみを別テーブルに分割
    2. モデルクラスで必要なモジュールのみを使⽤
    3. 挙動に合わせて設定

    View Slide

  26. 例 : メールアドレスとパスワードでのサインイン機能
    ビューはDeviseのデフォルトを使⽤
    c. テーブル分割⽅法の提案

    View Slide

  27. サインインに必要なカラムはemailとencrypted_password
    1. 機能に必要なカラムのみを別テーブルに分割
    c. テーブル分割⽅法の提案

    View Slide

  28. サインインに必要なカラムはemailとencrypted_password
    1. 機能に必要なカラムのみを別テーブルに分割
    c. テーブル分割⽅法の提案

    View Slide

  29. UserDatabaseAuthenticationクラス
    database_authenticatable: パスワードの暗号化や認証
    validatable: メールアドレス、パスワードのバリデーション
    2. モデルクラスで必要なモジュールのみを使⽤
    c. テーブル分割⽅法の提案

    View Slide

  30. Userクラス
    authenticatable: セッション管理やメソッドの提供
    2. モデルクラスで必要なモジュールのみを使⽤
    c. テーブル分割⽅法の提案

    View Slide

  31. Userクラス
    authenticatable: セッション管理やメソッドの提供
    2. モデルクラスで必要なモジュールのみを使⽤
    • authenticate_user!
    • user_signed_in?
    • current_user
    c. テーブル分割⽅法の提案

    View Slide

  32. Userクラス
    authenticatable: セッション管理やメソッドの提供
    → 認証⽅法が複数存在してもセッション管理はUserクラスが担う
    2. モデルクラスで必要なモジュールのみを使⽤
    • authenticate_user!
    • user_signed_in?
    • current_user
    c. テーブル分割⽅法の提案

    View Slide

  33. ルーティング
    サインイン画⾯のビューをデフォルトで使⽤
    3. 各種挙動に合わせて設定
    c. テーブル分割⽅法の提案

    View Slide

  34. ルーティング
    サインイン画⾯表⽰⽤パスを作成するために、
    emailとencrypted_passwordをもつ
    UserDatabaseAuthenticationモデルを
    devise_forの引数に指定する必要がある
    ※ devise_for : 指定されたモデルのためのルーティングを設定
    3. 各種挙動に合わせて設定
    c. テーブル分割⽅法の提案

    View Slide

  35. ルーティング
    こんな感じ
    3. 各種挙動に合わせて設定
    c. テーブル分割⽅法の提案

    View Slide

  36. アクション
    フォームからリクエストが送られるアクションの元実装
    3. 各種挙動に合わせて設定
    c. テーブル分割⽅法の提案

    View Slide

  37. アクション
    sign_inメソッド:resourceとして渡されたオブジェクトのクラスが
    セッション開始を担っている時、セッション開始
    3. 各種挙動に合わせて設定
    c. テーブル分割⽅法の提案

    View Slide

  38. アクション
    sign_inメソッド:resourceとして渡されたオブジェクトのクラスが
    セッション開始を担っている時、セッション開始
    resourceはdevise_forに渡したUserDatabaseAuthenticationクラス
    のオブジェクトとなる
    3. 各種挙動に合わせて設定
    c. テーブル分割⽅法の提案

    View Slide

  39. アクション
    sign_inメソッド:resourceとして渡されたオブジェクトのクラスが
    セッション開始を担っている時、セッション開始
    resourceはdevise_forに渡したUserDatabaseAuthenticationクラス
    のオブジェクトとなる
    → Userクラスがセッション管理を担っていない
    3. 各種挙動に合わせて設定
    c. テーブル分割⽅法の提案

    View Slide

  40. アクション
    ブロックを渡すとこの部分で実⾏してくれる
    特定の処理を差し込める
    3. 各種挙動に合わせて設定
    c. テーブル分割⽅法の提案

    View Slide

  41. アクション
    usersレコード由来のオブジェクトをsign_inメソッドに渡す処理を
    createメソッドのオーバーライド時に差し込む
    3. 各種挙動に合わせて設定
    c. テーブル分割⽅法の提案

    View Slide

  42. 以上の⼿順によって
    テーブル分割を⾏った上でサインイン機構を構築できた
    c. テーブル分割⽅法の提案

    View Slide

  43. ⽬次
    1. Deviseにおけるusersテーブルの分割
    a. Deviseとは
    b. Deviseをこんな⾵に使いたいな
    c. テーブル分割⽅法の提案
    d. テーブルを分割して実装してみた感想
    2. テーブル分割による柔軟な削除機構
    a. ユーザーの削除時に考慮したいこと
    b. 柔軟な削除機構を実現するための分割⽅法
    c. テーブル分割による削除機構の使⽤⽅法
    3. まとめ

    View Slide

  44. • Userクラスがスッキリして嬉しい気持ちになれた
    d. テーブル分割して実装してみた感想

    View Slide

  45. • Userクラスがスッキリして嬉しい気持ちになれた
    • 認証⽅法の追加時に新テーブル作成に思考が向きやすくなった
    d. テーブル分割して実装してみた感想

    View Slide

  46. • Userクラスがスッキリして嬉しい気持ちになれた
    • 認証⽅法の追加時に新テーブル作成に思考が向きやすくなった
    • とても難しい、、、今回の紹介は⼀番簡単な例
    d. テーブル分割して実装してみた感想

    View Slide

  47. • Userクラスがスッキリして嬉しい気持ちになれた
    • 認証⽅法の追加時に新テーブル作成に思考が向きやすくなった
    • とても難しい、、、今回の紹介は⼀番簡単な例
    • 未来の実装時間を使⽤している気持ちにもなった
    d. テーブル分割して実装してみた感想

    View Slide

  48. 2. テーブル分割による柔軟な削除機構

    View Slide

  49. ⽬次
    1. Deviseにおけるusersテーブルの分割
    a. Deviseとは
    b. Deviseをこんな⾵に使いたいな
    c. テーブル分割⽅法の提案
    d. テーブルを分割して実装してみた感想
    2. テーブル分割による柔軟な削除機構
    a. ユーザーの削除時に考慮したいこと
    b. 柔軟な削除機構を実現するための分割⽅法
    c. テーブル分割による削除機構の使⽤⽅法
    3. まとめ

    View Slide

  50. a. ユーザーの削除時に考慮したいこと
    ユーザーの削除機構の仕様決定時には多くを考慮する必要がある
    • ⼀度削除されたユーザーの復元が可能か
    • ユーザー削除時に関連するデータは残すか
    • プライバシー情報を削除しなければならないか

    View Slide

  51. これもテーブル分割で対応できるのでは?
    • ⼀度削除されたユーザーの復元が可能か
    • ユーザー削除時に関連するデータは残すか
    • プライバシー情報を削除しなければならないか
    a. ユーザーの削除時に考慮したいこと

    View Slide

  52. ⽬次
    1. Deviseにおけるusersテーブルの分割
    a. Deviseとは
    b. Deviseをこんな⾵に使いたいな
    c. テーブル分割⽅法の提案
    d. テーブルを分割して実装してみた感想
    2. テーブル分割による柔軟な削除機構
    a. ユーザーの削除時に考慮したいこと
    b. 柔軟な削除機構を実現するための分割⽅法
    c. テーブル分割による削除機構の使⽤⽅法
    3. まとめ

    View Slide

  53. b. 柔軟な削除機構を実現するための分割⽅法
    管理⽅法
    usersテーブルにactive_usersテーブルを関連付ける
    → usersテーブルのレコードがactive_usersを持つ時
    そのユーザーはアプリケーションを使⽤可能

    View Slide

  54. ユーザーの削除時
    関連するactive_usersテーブルのレコードを削除する
    b. 柔軟な削除機構を実現するための分割⽅法

    View Slide

  55. active_usersテーブルを採⽤した⽅法は柔軟な削除機構
    以下を決定できる
    • ⼀度削除されたユーザーの復元が可能か
    • ユーザー削除時に関連するデータは残すか
    • プライバシー情報を削除しなければならないか
    b. 柔軟な削除機構を実現するための分割⽅法

    View Slide

  56. ⽬次
    1. Deviseにおけるusersテーブルの分割
    a. Deviseとは
    b. Deviseをこんな⾵に使いたいな
    c. テーブル分割⽅法の提案
    d. テーブルを分割して実装してみた感想
    2. テーブル分割による柔軟な削除機構
    a. ユーザーの削除時に考慮したいこと
    b. 柔軟な削除機構を実現するための分割⽅法
    c. テーブル分割による削除機構の使⽤⽅法
    3. まとめ

    View Slide

  57. 削除するデータ、残すデータはこのように決定可能
    例1:プライバシー情報
    プライバシーであり、ユーザー退会時に⼀緒に削除したいデータ
    はactive_usersテーブルに配置
    c. テーブル分割による削除機構の使⽤⽅法

    View Slide

  58. 削除するデータ、残すデータはこのように決定可能
    例1:プライバシー情報
    プライバシーであり、ユーザー退会時に⼀緒に削除したいデータ
    はactive_usersテーブルに配置
    → active_usersのレコードと共にデータは削除される
    c. テーブル分割による削除機構の使⽤⽅法

    View Slide

  59. 削除するデータ、残すデータはこのように決定可能
    例2:ユーザーに関連するデータを残す
    c. テーブル分割による削除機構の使⽤⽅法

    View Slide

  60. 削除するデータ、残すデータはこのように決定可能
    例2:ユーザーに関連するデータを残す
    → active_usersのレコード削除時に関連データ(posts)も
    削除するか柔軟に変更可能
    c. テーブル分割による削除機構の使⽤⽅法

    View Slide

  61. ユーザーを「⼀時的にロックする」などのように状態を追加した
    い場合には新しく状態⽤のテーブルを作成する
    ⽅向に思考が向く
    → クラスの肥⼤化を防ぐ
    c. テーブル分割による削除機構の使⽤⽅法

    View Slide

  62. ⽬次
    1. Deviseにおけるusersテーブルの分割
    a. Deviseとは
    b. Deviseをこんな⾵に使いたいな
    c. テーブル分割⽅法の提案
    d. テーブルを分割して実装してみた感想
    2. テーブル分割による柔軟な削除機構
    a. ユーザーの削除時に考慮したいこと
    b. 柔軟な削除機構を実現するための分割⽅法
    c. テーブル分割による削除機構の使⽤⽅法
    3. まとめ

    View Slide

  63. まとめ
    1. Deviseのテーブルを分割するとUserクラスがスッキリする
    2. テーブル分割によってユーザーの削除機構を柔軟にできる

    View Slide

  64. ご清聴ありがとうございました

    View Slide