Upgrade to Pro — share decks privately, control downloads, hide ads and more …

入門 FormObject / An Introduction to FormObject #...

Avatar for Shu OGAWARA Shu OGAWARA
September 26, 2025

入門 FormObject / An Introduction to FormObject #kaigionrails

2025/09/26のKaigi on Rails 2025(Day 1)にて発表したスライドです。
https://kaigionrails.org/2025/talks/expajp

Avatar for Shu OGAWARA

Shu OGAWARA

September 26, 2025
Tweet

More Decks by Shu OGAWARA

Other Decks in Technology

Transcript

  1. • Shu Oogawara(@expajp ) • Engineering Manager Meetupコアスタッフ • 趣味

    • 秘境駅めぐり🚉 • 深夜ラジオ📻 • 筋トレ💪 2 ⾃⼰紹介 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025
  2. • 理解する • FormObjectの特徴 • FormObjectの使いどころ • 向き合う • どの現場にもある背景

    2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 9 FormObjectを理解して向き合う
  3. • 理解する • FormObjectの特徴 • FormObjectの使いどころ • 向き合う • どの現場にもある背景

    2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 10 FormObjectを理解して向き合う
  4. FormObjectの特徴 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 12

    明確な定義がない⾔葉 本発表での定義を説明します
  5. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 14 ①DBに紐付かないRubyオブジェクト

    class UserNewForm include ActiveModel::Model attr_accessor :email, :name validates :email, :name, presence: true before_save :email_to_lower_case def save # .. end private def email_to_lower_case # ... end end
  6. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 14 ①DBに紐付かないRubyオブジェクト

    class UserNewForm include ActiveModel::Model attr_accessor :email, :name validates :email, :name, presence: true before_save :email_to_lower_case def save # .. end private def email_to_lower_case # ... end end ActiveRecord::Base, ApplicationRecordを 継承してない
  7. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 15 class

    UserNewForm include ActiveModel::Model attr_accessor :email, :name validates :email, :name, presence: true before_save :email_to_lower_case def save # .. end private def email_to_lower_case # ... end end ②モデルと同じI/Fでコントローラから呼ばれる
  8. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 15 class

    UserNewForm include ActiveModel::Model attr_accessor :email, :name validates :email, :name, presence: true before_save :email_to_lower_case def save # .. end private def email_to_lower_case # ... end end Ac4veModel::Modelを includeしている ②モデルと同じI/Fでコントローラから呼ばれる
  9. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 16 ②モデルと同じI/Fでコントローラから呼ばれる

    class UserController < ApplicationController def create @form = UserNewForm.new(new_params) if @form.save redirect_to new_users_path, notice: 'ユーザを作成しました’ else render :new end end # ... end
  10. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 16 ②モデルと同じI/Fでコントローラから呼ばれる

    class UserController < ApplicationController def create @form = UserNewForm.new(new_params) if @form.save redirect_to new_users_path, notice: 'ユーザを作成しました’ else render :new end end # ... end Scaffoldで作った コントローラと 同じ⾒た⽬になる
  11. ※バリデーションとコールバックをまとめて、 ライフサイクル処理と呼ぶこととします (発表者がつけた独⾃の呼称) 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails

    2025 17 ③独⾃のライフサイクル処理を持てる class UserNewForm include ActiveModel::Model attr_accessor :email, :name validates :email, :name, presence: true before_save :email_to_lower_case def save # .. end private def email_to_lower_case # ... end end
  12. ※バリデーションとコールバックをまとめて、 ライフサイクル処理と呼ぶこととします (発表者がつけた独⾃の呼称) 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails

    2025 17 ③独⾃のライフサイクル処理を持てる class UserNewForm include ActiveModel::Model attr_accessor :email, :name validates :email, :name, presence: true before_save :email_to_lower_case def save # .. end private def email_to_lower_case # ... end end バリデーションと コールバックを持てる
  13. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 18 ④ビューの状態を保持できる

    引⽤︓[Kaigi on Rails 2024] Rails Way, or the highway - Speaker Deck, https://speakerdeck.com/palkan/kaigi-on-rails-2024-rails-way-or-the-highway?slide=76 (2025/09/26閲覧)
  14. • 発表者はFormObjectを使っている • 確かに、スコープが⼀部重なるレイヤはいくつかある • Application Model • View Model

    • Interactor • Service Object 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 19 わかってる⼈向け 他のレイヤじゃなくてFormObjectなの︖
  15. わかってる⼈向け︓他のレイヤじゃなくてFormObjectなの︖ 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 20

    (発表者の理解) モデルと 同じI/F ⼊⼒バリデー ション ビジネス ロジック処理 ビューの 状態保持 今回のFormObject ◯ ◯ ◯ ◯ Application Model ◯ ◯ ◯ × View Model × × × ◯ Interactor - ◯ × ◯ Service Object × × ◯ × • 他に良い⾔葉がないか、各レイヤの役割を整理した
  16. わかってる⼈向け︓他のレイヤじゃなくてFormObjectなの︖ 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 20

    (発表者の理解) モデルと 同じI/F ⼊⼒バリデー ション ビジネス ロジック処理 ビューの 状態保持 今回のFormObject ◯ ◯ ◯ ◯ Application Model ◯ ◯ ◯ × View Model × × × ◯ Interactor - ◯ × ◯ Service Object × × ◯ × • 他に良い⾔葉がないか、各レイヤの役割を整理した 今回する話に適したのはFormObjectだと判断
  17. • 理解する • FormObjectの特徴 • FormObjectの使いどころ • 向き合う • どの現場にもある背景

    2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 21 FormObjectを理解して向き合う
  18. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 24 FormObjectを使わない実装との⽐較

    使わない実装(=Rails Way) R C T M リクエスト レスポンス 使う実装 • リソース(R) – コントローラ(C) – モデル(M) – テーブル(T)がすべて1対1 • ライフサイクル処理はC(R)UDで⼀様
  19. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 24 FormObjectを使わない実装との⽐較

    使わない実装(=Rails Way) R C T M リクエスト レスポンス 使う実装 • リソース(R) – コントローラ(C) – モデル(M) – テーブル(T)がすべて1対1 • ライフサイクル処理はC(R)UDで⼀様 R C リクエスト
  20. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 24 FormObjectを使わない実装との⽐較

    使わない実装(=Rails Way) R C T M リクエスト レスポンス 使う実装 • リソース(R) – コントローラ(C) – モデル(M) – テーブル(T)がすべて1対1 • ライフサイクル処理はC(R)UDで⼀様 R C リクエスト F
  21. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 24 FormObjectを使わない実装との⽐較

    使わない実装(=Rails Way) R C T M リクエスト レスポンス 使う実装 • リソース(R) – コントローラ(C) – モデル(M) – テーブル(T)がすべて1対1 • ライフサイクル処理はC(R)UDで⼀様 R C リクエスト F ?
  22. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 24 FormObjectを使わない実装との⽐較

    使わない実装(=Rails Way) R C T M リクエスト レスポンス 使う実装 • リソース(R) – コントローラ(C) – モデル(M) – テーブル(T)がすべて1対1 • ライフサイクル処理はC(R)UDで⼀様 R C リクエスト F ?
  23. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 24 FormObjectを使わない実装との⽐較

    使わない実装(=Rails Way) R C T M リクエスト レスポンス 使う実装 • リソース(R) – コントローラ(C) – モデル(M) – テーブル(T)がすべて1対1 • ライフサイクル処理はC(R)UDで⼀様 R C リクエスト F ? F’ ?
  24. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 24 FormObjectを使わない実装との⽐較

    使わない実装(=Rails Way) R C T M リクエスト レスポンス 使う実装 • リソース(R) – コントローラ(C) – モデル(M) – テーブル(T)がすべて1対1 • ライフサイクル処理はC(R)UDで⼀様 R C リクエスト F ? レスポンス F’ ?
  25. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 24 FormObjectを使わない実装との⽐較

    使わない実装(=Rails Way) R C T M リクエスト レスポンス 使う実装 • リソース(R) – コントローラ(C) – モデル(M) – テーブル(T)がすべて1対1 • ライフサイクル処理はC(R)UDで⼀様 R C リクエスト F ? レスポンス F’ ? • モデルがFormObjectに置き換わっている • ライフサイクル処理はアクションごとに 変えられる
  26. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 25 Rails

    Wayの制約があると ユーザの⽬的が実現できないから なぜFormObjectが必要になるのか
  27. Rails Wayの制約があると、ユーザの⽬的が実現できない • Rails Wayの制約 • R-C-M-Tはすべて1対1に対応 • 制約を⽀えている仮定 •

    ⼀度に⾏うのはちょうどひとつのモデル操作でよい • ライフサイクル処理はひとつのパターンのみでよい 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 26
  28. Rails Wayの制約があると、ユーザの⽬的が実現できない • Rails Wayの制約 • R-C-M-Tはすべて1対1に対応 • 制約を⽀えている仮定 •

    ⼀度に⾏うのはちょうどひとつのモデル操作でよい • ライフサイクル処理はひとつのパターンのみでよい 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 27
  29. Rails Wayの制約があると、ユーザの⽬的が実現できない • Rails Wayの制約 • R-C-M-Tはすべて1対1に対応 • 制約を⽀えている仮定 •

    ⼀度に⾏うのはちょうどひとつのモデル操作でよい • ライフサイクル処理はひとつのパターンのみでよい 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 27 ここが”ひとつ”でないときがFormObjectの使いどころ
  30. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 29 FormObjectの使いどころ

    ⼀度に操作するモデルの数 0 1 2… ラ イ フ サ イ ク ル 処 理 の パ タ , ン 数 1 ①モデルを操作 しない Rails Way ②2個以上の モデルを操作 2… ③ライフサイクル 処理を分ける
  31. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 31 FormObjectの使いどころ

    ⼀度に操作するモデルの数 0 1 2… ラ イ フ サ イ ク ル 処 理 の パ タ , ン 数 1 ①モデルを操作 しない Rails Way ②2個以上の モデルを操作 2… ③ライフサイクル 処理を分ける
  32. • 例)メールフォーム 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025

    32 パターン① モデルを操作しない class FeedbackForm include ActiveModel::Model attr_accessor :title, :body validates :email, :name, presence: true def save return false if invalid? SomeMailer.feedback(title, body).deliver_later true end end 参考︓form objectを使ってみよう - メドピア開発者ブログ, https://tech.medpeer.co.jp/entry/2017/05/09/070758 (2025/09/26閲覧)
  33. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 33 FormObjectの使いどころ

    ⼀度に操作するモデルの数 0 1 2… ラ イ フ サ イ ク ル 処 理 の パ タ , ン 数 1 ①モデルを操作 しない Rails Way ②2個以上の モデルを操作 2… ③ライフサイクル 処理を分ける
  34. • 例)会社・社⻑・従業員を⼀画⾯で登録 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025

    34 パターン② 2個以上のモデルを操作する 参考︓accepts_nested_attributes_forを使わず、複数の⼦レコードを保存する - Money Forward Developers Blog, https://moneyforward-dev.jp/entry/2018/12/15/formobject/ (2025/09/26閲覧) class CompanyRegistrationForm include ActiveModel::Model attr_accessor :name, :address def save company.assign_attributes(company_params) company.employees << employees company.president = president company.save end end
  35. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 35 FormObjectの使いどころ

    ⼀度に操作するモデルの数 0 1 2… ラ イ フ サ イ ク ル 処 理 の パ タ , ン 数 1 ①モデルを操作 しない Rails Way ②2個以上の モデルを操作 2… ③ライフサイクル 処理を分ける
  36. • 例)ユーザの作成と編集でバリデーション分け 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025

    36 パターン③ ライフサイクル処理をアクションごとに分ける class UserNewForm include ActiveModel::Model attr_accessor :screen_name, :name validates :screen_name, :name, presence: true # ... end class UserEditForm include ActiveModel::Model attr_accessor :name validates :name, presence: true # ... end
  37. • 例)ユーザの作成と編集でバリデーション分け 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025

    36 パターン③ ライフサイクル処理をアクションごとに分ける class UserNewForm include ActiveModel::Model attr_accessor :screen_name, :name validates :screen_name, :name, presence: true # ... end class UserEditForm include ActiveModel::Model attr_accessor :name validates :name, presence: true # ... end
  38. まとめ︓FormObjectの使いどころ • ユーザの要求が Rails Wayを⽀える仮定に 反するとき • ⼀度に⾏うのはちょうどひとつ のモデル操作でよい •

    ライフサイクル処理はひとつの パターンのみでよい • 仮定がどう崩れるかに よってパターン分けが可能 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 37 ⼀度に操作するモデルの数 0 1 2… ラ イ フ サ イ ク ル 処 理 の パ タ , ン 数 1 ①モデル を操作 しない Rails Way ②2個以上 のモデル を操作 2… ③ライフ サイクル 処理を 分ける
  39. • 理解する • FormObjectの特徴 • FormObjectの使いどころ • 向き合う • どの現場にもある背景

    2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 38 FormObjectを理解して向き合う
  40. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 40 FormObjectは難しいけど

    Railsで仕事をするなら 向き合わなくてはならない FormObjectと向き合う
  41. Rails Wayの制約があると、ユーザの⽬的が実現できない • Rails Wayの制約 • R-C-M-Tはすべて1対1に対応 • 制約を⽀えている仮定 •

    ⼀度に⾏うのはちょうどひとつのモデル操作でよい • ライフサイクル処理はひとつのパターンのみでよい 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 41 再掲
  42. Rails Wayの制約があると、ユーザの⽬的が実現できない • Rails Wayの制約 • R-C-M-Tはすべて1対1に対応 • 制約を⽀えている仮定 •

    ⼀度に⾏うのはちょうどひとつのモデル操作でよい • ライフサイクル処理はひとつのパターンのみでよい 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 41 この仮定を満たすならば FormObjectを使わずRails Wayでよい
  43. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 42 最近「Scaffoldで⽣成して終わり」

    みたいな仕事ありました︖ さて、ここで質問です
  44. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 43 複雑な操作を

    ワンタップで実施したい 現代のWebアプリユーザが要求すること
  45. • 例)ECサイトの購⼊処理 • 在庫を減らす • 注⽂を作成する • 決済を⾏う • 決済記録を作成する

    • 通知を送信する 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 44 複雑な操作
  46. • 例)ECサイトの購⼊処理 • 在庫を減らす • 注⽂を作成する • 決済を⾏う • 決済記録を作成する

    • 通知を送信する 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 45 複雑な操作
  47. • 例)ECサイトの購⼊処理 • 在庫を減らす • 注⽂を作成する • 決済を⾏う • 決済記録を作成する

    • 通知を送信する 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 45 複雑な操作 別々のモデルが必要
  48. • 例)ECサイトの購⼊処理 • 在庫を減らす • 注⽂を作成する • 決済を⾏う • 決済記録を作成する

    • 通知を送信する 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 45 複雑な操作 現代ではワンタップが当たり前の処理もこれだけ複雑 別々のモデルが必要
  49. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 49 FormObjectは難しいけど

    Railsで仕事をするなら 向き合わなくてはならない FormObjectと向き合う 再掲
  50. 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 49 FormObjectは難しいけど

    Railsで仕事をするなら 向き合わなくてはならない FormObjectと向き合う 今回の発表はその⼀助になればと思って作りました 再掲
  51. • ユーザの要求がRails Wayの仮定に反するときが FormObjectの使いどころ • 使いどころは3つのパターンに分けられる • ①モデルを操作しない(0個) • ②2個以上のモデルを操作する

    • ③ライフサイクル処理をアクションごとに分ける • Railsで仕事をするなら、向き合わなくてはならない 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 50 全体のまとめ
  52. 「なんとなく」で使わず 「巨⼈の肩に乗る」姿勢を忘れずに 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025

    54 ありがとうございました Asturio Cantabrio, CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0>, via Wikimedia Commons
  53. 参考⽂献 • すがわらまさのり, 前島真⼀, 橋⽴友宏, 五⼗嵐邦明, 後藤優⼀, 2020, “パーフェクトRuby on

    Rails【増補改訂版】”, 技術評論社 • “form objectを使ってみよう - メドピア開発者ブログ”, https://tech.medpeer.co.jp/entry/2017/05/09/070758 • “Railsの仕組みを理解してモデルを上⼿に育てる - モデルを⾒つける、モデルを分割する良いタイミング –”, https://speakerdeck.com/igaiga/kaigionrails2024/ • “Form Objects with ActiveModel | Mathias Meyer”, https://www.paperplanes.de/2012/12/6/form-objects-with-activemodel.html • “Simplicity on Rails -- RDB, REST and Ruby - Speaker Deck”, https://speakerdeck.com/moro/simplicity-on-rails-rdb-rest-and-ruby • “[Kaigi on Rails 2024] Rails Way, or the highway - Speaker Deck”, https://speakerdeck.com/palkan/kaigi-on-rails-2024-rails-way-or-the-highway • “Ruby on Railsの正体と向き合い⽅ / What is Ruby on Rails and how to deal with it? - Speaker Deck”, https://speakerdeck.com/yasaichi/what-is- ruby-on-rails-and-how-to-deal-with-it • “ApplicationModel のある⾵景 / Rails with ApplicationModel - Speaker Deck”, https://speakerdeck.com/hshimoyama/rails-with- applicationmodel • “レールの伸ばし⽅ - Speaker Deck”, https://speakerdeck.com/willnet/rerufalseshen-basifang • “ActiveRecordのモデルが1つだとつらい #Rails – Qiita”, https://qiita.com/hanachin_/items/ba1dd93905567d88145c • “Form-backing objects for fun and profit - Pivotal Labs”, http://pivotallabs.com/form-backing-objects-for-fun-and-profit/ (リンク切れ。アーカ イブ︓ https://web.archive.org/web/20130617231634/http://pivotallabs.com/form-backing-objects-for-fun-and-profit/ ) • “フォームオブジェクトとの向き合い⽅/Grow Form Objects up - Speaker Deck”, https://speakerdeck.com/moro/grow-form-objects-up 2025/09/26 ⼊⾨ FormObject – Kaigi on Rails 2025 55