Rails開発パターン
Rails Development Patterns

!

!

吉岡ひろき
株式会社クレイirohiroki@gmail.com

概要
Ruby on Rails（以下「Rails」と呼ぶ）を採用したウェブアプリケーション開発には、プログラミング言語Rubyの特性や開発者およびライブラリの豊富さから、他のウェブアプリケーションフレームワークとは異なるノウハウがある。
本稿ではRailsアプリケーション開発の現場から見出された6つのパターンを紹介する。

!

はじめに期に抽出された6つのパターンを抜粋して紹介するものである。

!

パターンの抜粋!

各パターンの位置付け
パターンはそれぞれ主に適用する時期とレイヤが異なる。パターンと時期、レイヤの関係を図1に示す。
<公式情報>と<Rails Way>は開発の序盤から終盤まで、全レイヤに対して適用できる。<

Railsは2004年にDavid Heinemeier

公然のSQL>は全期間でモデル層に適用できる。

Hansson氏によって生み出され、現在も13人<レスポンシブテスト>は、中盤以降の全レイの中心的な開発者と3,000人以上の貢献者によっヤを対象とする。<コードの節制>は中盤以降て開発が続いているウェブアプリケーションに主にコントローラとモデルに対して適用でフレームワークである。2006年からは開発者

きる。最後に、<レール圏>は開発の終盤に顕と利用者のための国際的なカンファンレンス在化する問題を扱う。

「RailsConf」が毎年開催され、実際のビジネ
スでもクックパッド [1] やGitHub [2] といっ!

1. 公式情報た数百万人規模のユーザを持つサービスで採用されている。
このように、Railsは世界規模で利用されているにも関わらず、その開発ノウハウはRails
Best Practice [3] やRails3レシピブック [4] の状況
Railsの新機能や、よく知らない機能を使ってアプリケーションを実装している。

ようにベストプラクティスやレシピとして公開されるに留まり、パターン・ランゲージの形をとったものはなかった。
そこで、著者は7人の現役Railsアプリケーション開発者の協力を得て2014年9月からパターン・ランゲージ作成に取り組んでいる。本稿は、そのパターン・ランゲージの中から早問題
ウェブを検索して見つけた実装方法を真似ても、期待通りに動作しない。動作させるために更に検索したり試行錯誤したりして、時間を浪費する。

!

図1. パターンを適用する主な時期とレイヤ問題の原因
Q&AサイトのStack Overﬂow [5] には「ruby-on-rails」のタグがついた投稿が約18万件あり、PHPのウェブアプリケーションフ
レームワーク「cakephp」の約2万件、Javaのウェブ ア プ リ ケ ー シ ョ ン フ レ ー ムワ ー ク

フォース• Railsは互換性を犠牲にすることで積極的に新機能を導入したりコードの保守性を保ったりしている。
• 第三者による情報発信を制限したり、常に正しい情報を求めたりはできない。

「struts」の約3千件と比べると、Railsに関する情報がウェブ上に比較的豊富にあることが分かる。
一方、Railsは2008年4月から2014年12月までの間に4つのメジャーバージョンと9つのマイナーバージョンをリリースしており、その度に大小の非互換を生んでいる。

解決方法
Railsの機能について調べるときは、必ず公式の情報をあたる。

適用例

以上のことから、Railsに関してはウェブ検1. 公式ガイド）Rails 3のセキュリティ機能の索によって情報は得られるが、開発者のバー一つであるattribute̲accesibleはRails 4でジョンにおいては必ずしも正しくない状況がStrong Parametersに置き換えられたが、ウェ発生する。バージョンが一致していても、投ブ検索すると2015年1月時点でも稿者の誤解などによって間違った情報が掲載

attribute̲accesibleの使用方法を解説するブロされていることもある。

グ記事が見つかる。

このように意図せず古い情報を参照しない

扱うクラスの名前は「Product」を前提にする。

ようにするには、最初に公式のガイド [6] をRailsではこのような「Rails Way」と呼ばれあたればよい。

る慣例に従うことでコードの量が最小になる

2. リファレンス・マニュアル）Railsのリファように作られている。

レンス・マニュアルはウェブ上で公開されていRails Wayの範囲はデータベース層からプレる [7] 。また、「http://api.rubyonrails.org/

ゼンテーション層まで全域に広がっており、v4.1.0/」のようにURLにバージョン番号を付開発者が把握していない部分でRails Wayに反ければ、指定したバージョンのマニュアルをした決定をしてしまうと無駄が発生する。

参照することもできる。
3. リリースノート）バージョンアップによって変更された点は、公式ブログのリリースに関する投稿 [8] に書かれている。
4. ソースコード）以上の文書を読んでも不明な点については、GitHubで公開されているRailsのソースコード [9] を読むとよい。

結果初めて使う機能であっても期待通りに動作する。オプションなどの副次的な機能についても完全な情報が得られ、より適切に実装できる。

!

2. Rails Way

フォース• Rails WayはRailsの開発思想そのものであり、変更できない。
• Rails開発の入門者には開発思想が定着していないばかりでなく、開発思想の存在すら知らないことがある。

解決方法
他のフレームワークでは自由に選択できる設計や実装であっても、Rails Wayがないか確認し、可能な限り従う。

適用例
1. 命名規則）命名規則はRails Wayの代表で状況開発の序盤に様々な設計上の決定をし、実装を開始した。

ある。例えば、クラスの定義を含むファイルのパスはクラス名を元に走査される。逆に、クラス名から推測不能なパスにファイルを置き、明示的にパスを指定しなければLoadErrorが発生する。よって、特に理由がなければ命問題
Railsが想定外の処理を行い、解決のために調査したり余分なコードを追加したりして時間を浪費する。

問題の原因

名規則に従うべきである。
2. カラムの予約語）データベースのカラムに予約語がある。「type」というカラムは、一つ の テ ーブル に 複 数 の 型 を 格 納 す る 仕 組 み（Single Table Inheritance）に使われるため、それ以外の用途では避けなければならない。
3. Assets Pipeline）JavaScriptとスタイルシー

Railsは特定の実装には最適な方法があるとトは、開発段階では複数のファイルに分けていう仮定に基いて設計されており、その仮定おけるが、運用時には転送量削減のためにに 沿 っ た 実 装 を 推 奨 して い る [ 1 0 ] 。

Assets Pipelineという機構によって自動的にConvention over Conﬁgurationと呼ばれるソ圧縮、連結される。この機構を知らないと、フトウェア設計のポリシーもその一部であり、運用時の環境で発生した問題を解決できない例えばデータベースの「products」テーブルをことがある。Assets Pipelineはウェブアプリ

ケーションに必須の機構ではなく最適化に含まれるが、Railsは明示的に外さない限り最適化を行う方針を採っている。
4. コールバック）HTTPリクエストやDBアクセスを契機としたトランザクションの前後のコールバックには典型的な使い方がある。例
えば、認証にはトランザクションの前のコールバックを利用する。
その他のRails Wayは、Rails Guides [7] やAgile Web Development with Rails 4 [11] に記載されている。

フォース• コードの読みやすさや互換性のために、ActiveRecordを通じてSQLを構築するのは望ましい。
• 複数のデータベースの全ての機能を単一のライブラリでサポートすることはできない。

解決方法
利用するデータベースの機能を知り、必要に 応 じ て 最 適 な S Q L を 書 け る よ う に して おく。

結果最小のコードで最大の生産性を得られる。

適用例

また、Rails Wayに沿ったコードは書いた本人

1. UNION）ActiveRecordでSQLのUNIONを以外の開発者にも読みやすく、保守性が高ま

記述する方法は少なくとも2011年3月から議る。

論されているが、2014年12月の時点でも解決!

3. 公然のSQLしていない [12,13] 。よって、自分で書けるようにしておく必要がある。
2. N+1問題）「N+1問題」は、無駄なデータ状況
ActiveRecordの機能を使ってデータベースを操作している。

問題
意図通りのデータベースクエリを発行する
方法が分からずに時間を浪費したり、無意識のうちに高負荷のクエリを発行したりする。

問題の原因
ActiveRecordを使うと実際のSQLに比べて短いRubyコードでデータベースクエリを生成できる。また、実際のSQLの語順や、データベースの種類に依存せずに記述できるなどのメリットもある。
しかし、より複雑なクエリを生成するためにRubyコードも複雑になることは避けられない。また、ActiveRecordでは特定のデータベースに固有の最適化手法を利用できない場合もある。

ベースクエリを発生させる典型的な問題である [14] 。例えば、下のようにBookとAuthorと対応するテーブルが定義されているとき、class Author < ActiveRecord::Base!
end!

!

class Book < ActiveRecord::Base!
belongs_to :author!
end!

!

create_table :authors do |t|!
t.string :name!
end!

!

create_table :books do |t|!
t.string :name!
t.references :author, index: true!
end!

!次のようなコードは合計11回のクエリを発生する。

!
!

# コントローラ!
@books = Book.last(10)!
# ビュー!
<% @books.each do |book| %>!
<tr>!
<td><%= book.name %></td>!
<td><%= author.name %></td>!
</tr>!

!実際に発行されるクエリは下の通り。
!
<% end %>!

SELECT`books`.* FROM `books`
ORDER BY `books`.`id` DESC LIMIT 10!
SELECT`authors`.* FROM `authors`
WHERE `authors`.`id` = 1 LIMIT 1!
SELECT`authors`.* FROM `authors`
WHERE `authors`.`id` = 2 LIMIT 1!
…!
SELECT`authors`.* FROM `authors`
WHERE `authors`.`id` = 9 LIMIT 1!
SELECT`authors`.* FROM `authors`
WHERE `authors`.`id` = 10 LIMIT 1!

!コントローラのコードを以下のように修正することで、クエリは2回になる。

!
!修正後のクエリは以下の通り。
!

@books = Book.includes(:author)\!
.last(10)!

SELECT`books`.* FROM `books`
ORDER BY `books`.`id` DESC LIMIT 10!
SELECT `authors`.* FROM `authors`
WHERE `authors`.`id` IN (10, 9, 8, 7,6, 5, 4, 3, 2, 1)!

ドのジェネレータによって製品コードと共にテストコードの雛形も生成されるなど、テストコードを書きやすくする仕組みが備わっている。
このように、Railsアプリケーションにおいてテストコードを書くことは推奨されているが、個々のテストケースの価値やテスト実行のコストについてはRails Guidesでも言及されておらず、Rails初心者が実際に問題に遭遇するまでテスト実行時間について検討する機会が提供されていない。

フォース• 円滑な開発や問題の早期発見のために、テストコードを書くことは望ましい。
• 全く実行時間のかからないテストコードは存在しない。

解決方法
開発の初期からテストコードの費用と効果結果を評価しながら開発する。

データベースクエリに起因する問題により早く気付けるようになり、リリース後に障害適用例が発生するリスクを下げる。

1. インテグレーションテスト）Railsがサポー!

4. レスポンシブテスト

トするテストのうち、ブラウザをエミュレートし、JavaScriptの実行からデータベースアクセスまで全てのレイヤを実行するインテグレー

状況

ションテストは最も実行時間がかかるため、全ての条件分岐を網羅するテストをインテグ

アプリケーションの開発が進み、テストコードが増えてきた。

レーションテストで書くと、他の種類のテストで書いた場合よりも実行時間が長くなってしまう。インテグレーションテストは、全レ問題
テストの実行時間が長くなり、開発者のスイヤを実行しレイヤ間の接続を確認できることを活かし、スモークテストに利用するとよトレスとなったり、問題の発見が遅れたりす

い。

る。

2. ビジネス上の価値の反映）テストケースの価値は、テスト対象のビジネス上の価値に依問題の原因
Railsにはテストコーディングを補助するヘルパーメソッドが用意されていたり、雛形コー存する。例えば、ビジネスの成立に必須ではない代替手段のある機能は、カバー率を上げて実行時間をかける価値は低い。逆に、ビジネスの成立に不可欠な機能のカバー率を上げ

Development with Rails 4[11]でもモデルとるべきである。

コントローラにコードを書いている。

3. モックとスタブの利用）どのレイヤのテス

ところが、現実のソフトウェアはより複雑トを書く場合でも、テスト対象を明確にし、な場合があり、アプリケーションによって適対象外のコードを実行しないことで実行時間切なアーキテクチャは異なり [15] 、モデルとを最短にできる。実行するコードを最小にすコントローラに書くことが最適とは限らない。

るためには、モックやスタブの利用を検討す!

る。
例えば、ルーティングをテストするコードでデータベースにアクセスしてはいけない。永続化層はスタブにし、実際の入出力処理の実行を回避すべきである。

結果
アプリケーションを拡張してもテストの実行時間は適切な範囲に保たれる。リグレッショ
ンは早期に発見され、開発者は安心して開発を進められる。

!

5. コードの節制
状況
ある程度の期間に渡って機能の拡張や改修を繰り返している。

フォース• 機能の拡張や改修を繰り返すことでコードは増加する。
• 世の中の全てのアーキテクチャをサポートすることはできない。

解決方法
モデルとコントローラの責務を明確にし、その範囲を越える処理は、より適切なクラスやレイヤに割り当てる。

適用例
1. サービスレイヤ）例えば、複数のモデル（仮にAとBとする）が関与する処理について考える。この処理はAの責務ともBの責務とも言い切れない場合がある。Aの責務としてAからBを参照すれば、AとBが密結合し、保守性が低問題
モデル層とコントローラ層のクラスが肥大化し、改修や保守が困難になる。

問題の原因

下するし、逆もまた同様である。だからといって、この処理をコントローラ層に置くと、コントローラ層が肥大化する。このように、複数のモデルが関わる処理はサービスと呼ばれるレイヤに切り出す解決方法がある [16] 。
2. ActiveSupport::Concern）Rails 4.0ではモ

Railsにはコードの雛形を生成する機能が備

デルまたはコントローラクラスの機能の一部わっており、その機能を使うとモデルとコンをモジュールに切り出す仕組みである

トローラ、ビューの各層のファイルができる。

ActiveSupport::Concernと、専用のディレクビュー層のファイルはHTMLテンプレートなのトリが導入された。ActiveSupport::Concern

で、トランザクションの処理を書くには適さを使うことで、モデルまたはコントローラ本ない。逆に、全てのトランザクションは生成来の責務ではない処理や、複数のクラスに共されたモデルとコントローラに書いて実現で通する処理を独立したモジュールに書ける。

きる。実際に、David Heinemeier Hansson

3. Single Responsibility Principle）Single

が著者として参加している入門書Agile Web

Responsibility Principle（以下SRP）はオブ
ジェク指向プログラミングの原則であり、一つのクラスが一つの責務を持つことを表す。

め、デバイスの性能を最大限に出せるとは限

SRPの侵害はモデルやコントローラの肥大化らない。の原因の一つである。特に、SRPに則ったモつまり、Railsで機能を実現できたとしてデル の リ フ ァ クタ リ ング 方 法 に つ いて は 、も、アプリケーションに必要な性能が出るとBryan HelmkampがRubyKaigi 2013で発表しは限らない。また、アプリケーション開発のている [17] 。

初期段階では十分な性能が出ていても、機能拡張などの改修を重ねることで性能が出なく

結果アプリケーションの拡張が続いてもクラスの責務は明確で読みやすい。コードは継続的なる場合もある。

フォースな保守に耐える。

• Railsのアーキテクチャを変えることはできない。

6. レール圏• アプリケーションの要件も変えられない。

!

状況
計画の大半の機能を実装し終え、ユーザビリティテストや負荷テストを開始した。

問題

解決方法ウェブアプリケーションが適したケースにだけRailsを採用する。また、アプリケーションを拡張する際はRailsで実現する範囲を適切に制限し、その範囲を越える部分には他のソフトウェアを利用する。

機能は実現できたが、操作感や表現力などの非機能要件を満たせない。

問題の原因

適用例
1. スマートフォン）スマートフォンやタブレッ
トといったモバイルデバイスは同世代のパソ

Railsは「Creating a weblog in 15コンに比べてJavaScriptやスタイルシートの処minutes」という動画 [18] で宣伝するなど、理に時間がかかるため、ユーザ体験を損ねるアイデアを素早く形にできることを強みの一一因になる。特に、スマートフォンは通信環つとしている。また、ウェブアプリケーショ境が不安定なので、サーバからファイルを取ンフレームワークであることにより、ユーザり寄せる必要のあるウェブアプリケーションにはブラウザだけで使える利便性を、開発者は不利になる。にはHTMLとCSSだけ知っていればプレゼンテー

2. コンシュマー向けアプリケーション）コンションできる作りやすさを提供している。こシュマー向けのアプリケーションはビジネスのように、Railsには開発者に「簡単にできなどの特定用途向けのものに比べて高い表現る」と思わせる要素がある。

力を求められるケースが多い。

しかし、ウェブアプリケーションはユーザつまり、モバイルデバイスやコンシュマーインターフェースとデータがネットワークを介を対象としたアプリケーションを開発する際して分散しているため、その両方が同一のデバには、応答性能や表現力の低さと開発しやすイス上にあるネイティブアプリケーションにさを検討して採用するフレームワークを決定比べて応答性能が劣る。また、ブラウザの表する。

現力はHTMLの仕様によって制限されているたRailsを採用して十分な操作性や表現力を実[4]

高橋征義, 松田明, 諸橋恭介. 2011.

現できるケースもあるが、相応のコストを見Rails3 レシピブック. ソフトバンククリエイティ

積もっておくべきである。

ブ.
[5]stack exchange inc. Stack Overﬂow.

http://stackoverﬂow.com/

結果
様々なアプリケーションを開発するなかで、開発しやすさや実装の速さといったRailsのメリットだけを享受し、リリース直前に非機能要件を満たせなくなるなどのトラブルを回避できる。

!

まとめ
Railsアプリケーション開発を対象とした作[6]

Ryan Bigg, 他. Rails Guides. http://guides.rubyonrails.org/[7]

David Heinemeier Hansson, 他.

Ruby

on

RailsAPI.

http://api.rubyonrails.org/[8]

David Heinemeier Hansson, 他.

Riding Rails. http://weblog.rubyonrails.org/
releases/[9]

David Heinemeier Hansson, 他.

成中のパターン・ランゲージの中から、6つのrails/rails. https://github.com/rails/rails

パターンを紹介した。

[10]

今後は、本稿で述べなかったパターンを含

Ryan Bigg, 他. Getting Started with

Rails. http://guides.rubyonrails.org/

めたパターン・ランゲージ全体を完成させ、

getting̲started.html

利用者にとってより使いやすい形式で公開す[11]

ると共に、より多くのRailsアプリケーション

Heinemeier Hansson. 2014. Agile Web

開発者に意見を求めて改善していく予定であDevelopment with Rails 4. The Pragmaticる。

Bookshelf.

!

[12]

謝辞
パターンの案を提供し、アイデアの本質をSam Ruby, Dave Thomas, DavidLighthouse Import, 他. ActiveRecord

UNION left out. https://github.com/rails/
rails/issues/939[13]James Sanders, 他. Make union/

探求する議論に参加してくれた赤松祐希氏、except/intersect chainable as

浅海拓来氏、朝倉寛史氏、櫻井達生氏、白土SelectManager. https://github.com/rails/

慧氏、三村益隆氏、諸橋恭介氏に心から感謝

arel/pull/320

する。また、シェパードとして構成から記法[14]まで丁寧にレビューしてくださった坂本一憲博

Associations. http://guides.rubyonrails.org/

士にも感謝申し上げる。

active̲record̲querying.html#eager-!

loading-associations

リファレンス[1]

クックパッド株式会社. クックパッド.

http://cookpad.com/[2]

GitHub, Inc. GitHub. https://github.com/[3]

Ryan Bigg, 他. Eager Loading

Xinmin Labs. Rails Best Practices.

http://rails-bestpractices.com/[15]

マーチン・ファウラー. 2005. エンター

プライズ アプリケーションアーキテクチャパ
ターン. 翔泳社.
[16]

エリック・エヴァンス. 2011. エリッ

ク・エヴァンスのドメイン駆動設計. 翔泳社.
[17]

Bryan Helmkamp. Refactoring Fat

Models with Patterns. http://rubykaigi.org/
2013/talk/S32[18]

David Heinemeier Hansson,

Creating a weblog in 15 minutes. http://showmedo.com/videotutorials/video?
name=rubyWeblogIn15Mins