Railsデザインパターン徹底解説:プレゼンターとサービスオブジェクトの実践的な使い方
なぜRailsにはデザインパターンが必要なのでしょうか?
RailsのアーキテクチャであるMVC(Model-View-Controller)は、コードを配置するための基本的な構造を提供してくれます。
しかし、それだけでは十分ではありません。
本来は情報を表示することだけが役割のはずのビューが、ロジックだらけの巨大なファイルへと成長してしまう。
コントローラーにも、本来の責務以外の詳細な処理が詰め込まれていく。
解決策は何でしょうか?
こうした問題を解決するために、デザインパターンの形で2つのソリューションが生まれました。
- プレゼンターパターン(Presenter Pattern)
- サービスオブジェクトパターン(Service Object Pattern)
実装方法については人それぞれ意見が分かれるところですが、この記事では実際にうまく機能しているバージョンをご紹介します。
それでは、これらのパターンを見ていきましょう!
Railsでのプレゼンターの使い方
ビューの役割は「プレゼンテーション」、つまりHTML・CSS・ERB(Embedded Ruby)による表示です。
ビュー内にActiveRecordのクエリを書くべきではありません。
さらにほとんどのロジックも排除すべきです。ビューをできる限りクリーンで扱いやすい状態に保ちたいなら。
ここでいう「ロジック」とは、if文や三項演算子を使った条件分岐による判断処理のことです。
では、どうすればいいのでしょうか?
その答えは次の通りです。
ビュー内のロジックに対処する最初のツールは、ヘルパー(helpers)です。
複数のビューで共通して使うグローバルなフォーマットメソッドがある場合、ヘルパーは非常に便利です。
例えば:
Markdownのレンダリング、特定フォーマットでの日付表示、テキストから特定の単語を除去する処理などが挙げられます。
具体的にはこんな感じです:
module DateHelper
def date_as_month_and_year(date)
date.strftime("%B %Y")
end
end
このコードはapp/helpersフォルダ配下のdate_helper.rbファイルとして保存します。
ここでひとつヒント:
ヘルパーメソッドへの入力は必ず引数として渡し、インスタンス変数に依存しないようにしましょう。
この習慣が、後々多くのトラブルからあなたを守ってくれます。
ただし、ヘルパーメソッドには限界があります。特に、ビューのあらゆるフォーマット処理にヘルパーを使い始めると顕著です。
メソッドが際限なく増えていき、組織化された構造を持たないカオスな状態になりがちです。
そこで登場するのが次のソリューションです!
複雑な条件分岐とフォーマット処理をプレゼンターオブジェクトに置き換える
例えば、次のようなビューがあるとします。
<p>
Post title: <%= post.title.gsub("forbidden word", "") %>
<%= link_to "Read post", post, class: "w-75 p-3 text-#{post.draft? ? "orange" : "green"} border-#{post.draft? ? "orange" : "green"}" %>
</p>
かなり短いビューですよね?
しかし、三項演算子のコード重複によって、非常に複雑に感じられます。
これは良くありません。
そこで、この問題を解決するプレゼンタークラスを作成しましょう。
やり方は以下の通りです:
class PostPresenter
def initialize(post)
@post = post
end
def title_without_forbidden_words
@post.title.gsub("forbidden word", "")
end
def css_color
@post.draft? ? "orange" : "green"
end
end
このファイルはapp/presenters/post_presenter.rbとして保存します。presentersフォルダがまだなければ作成してください。
これでビューを書き換えられます。
こんな感じになります:
<% presenter = PostPresenter.new(post) %>
<p>
Post title: <%= presenter.title_without_forbidden_words %>
<%= link_to "Read post", post, class: "w-75 p-3 text-#{presenter.css_color} border-#{presenter.css_color}" %>
</p>
これで完成です!
- ビューからすべてのロジックを取り除きました
- フォーマットや判断処理に、意味のある名前を付けました
- このクラスは他のビューでも再利用でき、コードの重複を避けられます
これがRailsにおけるプレゼンターの使い方です 🙂
サービスオブジェクトの使い方
コントローラーの役割は、他のコンポーネントに「何をすべきか」を指示することだけです。ツイートの送信方法、顧客への課金処理、PDFファイルの生成方法といった詳細を知っている必要はありません。
こうした操作はサービスオブジェクトに委譲すべきです。
私の定義によれば、サービスオブジェクトとは「あるアクションを完了するためのロジックをカプセル化したRubyモジュール」のことです。
例:
module TwitterService
def self.send_welcome_message(twitter_handle)
client.update("@#{twitter_handle} welcome to 'Oranges & Apples', we hope you enjoy our juicy fruit!")
end
def self.client
@client ||= Twitter::REST::Client.new do |config|
config.consumer_key = "..."
config.consumer_secret = "..."
config.access_token = "..."
config.access_token_secret = "..."
end
end
end
慣習としては、app/servicesフォルダ配下にtwitter_service.rbのようなファイル名で保存します。
では、どう使うのでしょうか?
Railsはapp/配下のすべてをオートロードするため、このコードはコントローラーからすぐに利用できます。
使用例:
class UsersController
def create
# ...
TwitterService.send_welcome_message(user.twitter_handle)
end
end
これがサービスオブジェクトパターンの実際の活用例です。
まとめ
この記事では、賢く使えばプロジェクトのコード品質を向上させてくれる、2つの便利なRailsパターンを学びました。
- プレゼンター: ビューからロジックとフォーマット処理を追い出し、再利用可能な形に整理する
- サービスオブジェクト: コントローラーからビジネスロジックを切り離し、単一責任の原則に従わせる
さあ、今度はあなたが自分のプロジェクトに適用してみる番です 🙂
最後までお読みいただき、ありがとうございました。
-
AWS LambdaでRailsアプリを動かす方法!Lambyを使ったデプロイ手順を徹底解説
サーバーレスコンピューティングは、サーバーの管理やプロビジョニングの作業をクラウドプロバイダーにオフロードできる仕組みとして、多くのテックチームで急速に標準になりつつあります。その代表格がAWS Lambdaです。LambdaはNode.js、Java、Python、Rubyといった主要なプログラミング言語を幅広くサポートしています。しかし、言語そのものだけでなく、それらの言語で構築されたフレームワークの機能に依存するサーバーレス関数を実行したいケースもあるでしょう。 本記事では、RailsアプリケーションをAWS Lambda上で実行する方法を解説します。サーバーレスコンピューティングとAW
-
Rails5でのAngularの使用
あなたは前にその話を聞いたことがあります。分散型で完全に機能するバックエンドAPIと、通常のツールセットで作成されたフロントエンドで実行されているアプリケーションがすでにあります。 次に、Angularに移動します。または、AngularをRailsプロジェクトと統合する方法を探しているだけかもしれません。これは、この方法を好むためです。私たちはあなたを責めません。 このようなアプローチを使用すると、両方の世界を活用して、たとえばRailsとAngularのどちらの機能を使用してフォーマットするかを決定できます。 構築するもの 心配する必要はありません。このチュートリアルは、この目的のた