第4章 サンプルアプリの仕様
この章のねらい
本書では、1 つの Rails アプリケーションを最初から最後まで育てながら Hotwire を学びます。題材は、チーム向けタスク管理アプリ Relayです。
この章では、Relay の題材を選んだ理由、何を作って何を作らないか、モデルと画面の構成、そして各部でどこを Hotwire 化していくかを共有します。コードはまだ書きません。先に全体像を持っておくことで、以降の章で「いま何のためにこの機能を使っているのか」を見失わずに済みます。
4.1 題材の選定
Hotwire を学ぶ題材には、次の条件がそろっていると都合がよいです。
- 一覧画面があり、検索や絞り込みが自然に登場する
- データ件数が多く、ページネーションの必要が出てくる
- レコードに状態(ステータス)があり、バリデーションが意味を持つ
- 複数のユーザーが同じ画面を見て、リアルタイム更新が活きる
ToDo アプリでは薄すぎ、SNS のような題材では発散しすぎます。その中間にあるのがタスク管理です。プロジェクトの下にタスクがぶら下がり、タスクにステータス・担当者・タグ・コメントが付く、という構造は、上の条件をすべて自然に満たします。
そこで本書では、チーム向けタスク管理アプリ Relay を育てます。
4.2 題材に必要な条件
4.1 で挙げた条件を、Relay のどの要素が満たすかを対応させておきます。
| 必要な条件 | Relay での実現 |
|---|---|
| 一覧+検索 | タスク一覧をキーワード・ステータス・タグで絞り込む |
| 件数の多さ | seed で約 150 件のタスクを投入し、ページネーションを学ぶ |
| ステータス遷移 | タスクの status(todo / in_progress / done)を切り替える |
| 複数ユーザー | 同じタスクを複数人が開き、コメントや更新をリアルタイムに共有する |
この 4 点が、第7部「実務で使う Hotwire UI パターン」の題材にそのままつながります。
4.3 本書でやること / やらないこと
実務アプリを丸ごと作ろうとすると、Hotwire と関係のない作り込みが増え、学習の焦点がぼやけます。本書では、Hotwire を理解するうえで意味のある範囲に絞ります。何を捨てたかをはっきりさせておきます。
| 要素 | 本編での扱い | 理由 |
|---|---|---|
| 認証 | 入れる(最小) | current_user が担当者、コメント投稿者、broadcast 範囲に必要になるため。Rails 標準の認証機能を使う。 |
| 認可 | 本編は最小、深掘りは第31章 | 単一チーム前提にして、Pundit などの認可ライブラリは持ち込まない。 |
| マルチテナント / メンバー管理 | 入れない | Membership を入れると認可が重くなるため。1 チーム全員が同じプロジェクトを見られる前提にする。 |
| タグ | 入れる(第23章で UI 化) | 検索と絞り込みに必要。初期 CRUD ではなく、必要になった章で導入する。 |
| 担当者 | 入れる | 実務的なフィルタ、表示、broadcast の文脈に必要。Task から User への nullable 参照に留める。 |
| コメント | 入れる(UI は第16章以降) | Turbo Streams とリアルタイム更新の主役。モデルは第5章で用意し、UI は後で育てる。 |
| 通知 | 永続化しない | 本編では Action Cable による即時通知に限定する。永続 Notification モデルは付録候補にする。 |
このうち「入れるが UI は後で作る」もの(タグ・コメント)があるのは意図的です。最初からすべての画面を作り込むと、Hotwire の各機能を「なぜ使うのか」が見えにくくなります。必要になった章で初めて UI を足すことで、機能を使う動機が明確になります。
4.4 モデル構成
Relay のモデルは次の 6 つです。関連は素直で、Rails の学習者がつまずかない範囲に収めています。
| モデル | 主な属性 | 役割 |
|---|---|---|
User | name, email_address, password_digest | ログインユーザー、担当者、コメント投稿者 |
Project | name, description | タスクをまとめる単位 |
Task | project_id, title, description, status, assignee_id, due_on | 本書の中心リソース |
Comment | task_id, user_id, body | Turbo Streams と broadcast の題材 |
Tag | name | 検索・絞り込みの題材 |
Tagging | task_id, tag_id | Task と Tag の中間モデル |
関連をまとめると次のとおりです。
Projectは複数のTaskを持ちます。Taskは複数のCommentを持ちます。TaskはTaggingを介して複数のTagと多対多です。Taskは担当者としてUserを 1 人持ちます(未割り当ても許します)。Commentは投稿者としてUserを 1 人持ちます。
Task#status は todo、in_progress、done の enum にします。ステータスは検索、絞り込み、バリデーション、表示の出し分け、通知のすべてに使えるため、本書のハンズオンを支える軸になります。
なお User は Rails 標準の認証ジェネレータ(bin/rails generate authentication)で作ります。このジェネレータは User と Session を中心に、認証に必要な一式(コントローラ・ビュー・ルーティング・マイグレーション・bcrypt の導入)をまとめて生成します。これらは認証インフラなので、上表の Relay のドメインモデル(Project や Task など)とは区別して扱います。
具体的には、現在のユーザーを保持する
Current、ログインを扱うSessionsController、パスワード再設定のPasswordsController、認証ヘルパーをまとめた concern、再設定メール用のPasswordsMailerなども生成されます。
また、生成直後の User は email_address と password_digest を持ちますが name は含まないため、本書では生成後に name を追加します。詳しい手順は第5章で扱います。
4.5 主要画面
Relay の主要な画面と、そこで主に使う Hotwire の技術を対応させると次のようになります。どの画面でどの技術を使うかは、第3部以降で 1 つずつ実装していきます。
| 画面 | 役割 | 主に使う技術 |
|---|---|---|
| プロジェクト一覧 / 詳細 | 入口、通常のページ遷移の題材 | Turbo Drive |
| タスク一覧(リスト / ボード) | 検索、絞り込み、ページネーション | Turbo Frames + Stimulus |
| タスク詳細(サイドバー) | 遅延読み込み、タブ | Turbo Frames |
| タスク作成 / 編集 | バリデーション UX、モーダル | Turbo Frames + Turbo Streams + Stimulus |
| コメント欄 | 追記、削除、リアルタイム更新 | Turbo Streams + Action Cable |
| 通知トースト / flash | 操作結果、他者の更新 | Turbo Streams + Stimulus + Action Cable |
ここで大切なのは、1 つの画面が複数の技術の組み合わせでできていることです。Hotwire は「どれか 1 つを選ぶ」ものではなく、画面の要件に応じて使い分けるものだと、この表が示しています。
4.6 Hotwire 化するポイント
Relay は、まず通常の Rails の CRUD として作ります(第5章)。その時点では、画面遷移のたびにページ全体が再描画される、ごく普通の Rails アプリです。
そこから、次のような順序で段階的に Hotwire 化していきます。
- ページ遷移とフォーム送信を Turbo Drive で速く見せる(第3部)
- タスクの一覧・詳細・編集を Turbo Frames で部分更新する(第4部)
- 作成・更新・削除・コメントを Turbo Streams で画面遷移なしに反映し、リアルタイム更新まで広げる(第5部)
- サーバーとのやり取りが要らない振る舞いを Stimulus で足す(第6部)
- 検索・ページネーション・モーダル・通知など、実務的な UI に仕上げる(第7部)
重要なのは、いきなり完成形を作らないことです。通常の Rails アプリを出発点にして、「ここはページ全体を再描画しなくてよいはずだ」と気づいた箇所を、必要な技術で置き換えていきます。この進め方そのものが、実務で Hotwire を導入するときの考え方になります。
4.7 各部との機能対応表
本書のどの部で、Relay のどこを作る・変えるかを一覧にします。読んでいる途中で「いま全体のどこにいるのか」を見失ったら、この表に戻ってください。
| 部 | Relay で作る / 変える対象 | 主な技術 |
|---|---|---|
| 第3部 Turbo Drive | プロジェクト / タスクのページ遷移とフォーム送信 | Turbo Drive |
| 第4部 Turbo Frames | タスクのインライン編集、サイドバー詳細の遅延読み込み | Turbo Frames |
| 第5部 Turbo Streams | コメント追記・削除、タスクの作成・更新・削除、件数更新、Cable 配信 | Turbo Streams + Action Cable |
| 第6部 Stimulus | タスクフォームの補助、ドロップダウン、文字数、確認 UI | Stimulus |
| 第7部 UI パターン | タスクの検索・絞り込み、ページネーション、フォーム UX、モーダル / タブ / ドロップダウン、通知トースト | 総合 |
| 第8部 保守 | System Test、デバッグ、N+1、認可とセキュリティ | 横断 |
| 第9部 Native | Relay をネイティブシェルで包む | Hotwire Native |
| 第10部 選定 | Relay を題材にアンチパターンと SPA 比較を振り返る | 採用判断 |
第7部の章ごとの内訳(どの章で検索を作り、どの章でモーダルを作るか)は、第7部の各章で詳しく示します。
4.8 完成形の操作ストーリー
完成した Relay を、ユーザーの操作の流れで見てみます。これがゴールのイメージです。
- ログインすると、プロジェクト一覧が表示されます。リンクをたどってもページ全体は再読み込みされず、画面が一瞬で切り替わります(Turbo Drive)。
- プロジェクトを開くと、タスクの一覧が出ます。上部の検索ボックスにキーワードを入れると、一覧だけがその場で絞り込まれ、URL にも条件が反映されます。共有してもリロードしても同じ結果が出ます(Turbo Frames + Stimulus)。
- 一覧をスクロールすると、続きのタスクが追加で読み込まれます(ページネーション)。
- タスクの行で「編集」を押すと、その行だけがフォームに変わります。入力が不正なら、ページ遷移せずにエラーがその場に表示されます(Turbo Frames + バリデーション UX)。
- 「新規作成」を押すとモーダルが開きます。保存すると、モーダルが閉じ、一覧の先頭に新しいタスクが追加され、件数も更新されます(Turbo Streams)。
- タスクを開いてコメントを書くと、同じタスクを見ている他のメンバーの画面にも、リアルタイムでコメントが追加されます(Action Cable)。
- 操作のたびに、画面の隅にトーストが出て、少し経つと自動で消えます(Turbo Streams + Stimulus)。
この一連の操作は、すべてサーバーが HTML を返すことで実現します。JSON を組み立ててクライアントで描画する、という作りにはなっていません。これが HTML over the wire の体験です。
4.9 本書で採用する JavaScript 構成
Rails で JavaScript を扱う方法はいくつかありますが、本書では importmap を基本にします。
importmap は Rails の標準構成で、ビルド工程(npm や bundler 相当の JavaScript ビルド)を必要としません。学習者の環境差を最小化でき、Hotwire の本質に集中できます。
一方で、外部の npm パッケージを本格的に使う場合は jsbundling 構成が向く場面もあります。本書では第22章「外部ライブラリと連携する」で、importmap での外部ライブラリの読み込み方を扱います。構成そのものの詳しい比較は第6章で行います。
この章で決めたモデル名・画面名・ステータス値は、以降のすべての章で参照します。手を動かす前に、
Task#statusがtodo/in_progress/doneであること、タグとコメントの UI は後の章で作ることだけは覚えておいてください。
参考資料
- Hotwire: https://hotwired.dev/
- Rails Guides: https://guides.rubyonrails.org/
- Rails ガイド「Active Record の関連付け」: https://guides.rubyonrails.org/association_basics.html