【Rails】なぜファットコントローラを避けるべきなのか?腹落ちするまで向き合ってみた
はじめに
お疲れ様です!
おおくまです!
今回は、「なぜファットコントローラを避けるべきなのか?腹落ちするまで向き合ってみた」ということで、私なりにファットコントローラについてまとめてみました!
少しでも皆様の参考になりますと幸いです!
対象読者
注意点
なぜファットコントローラについてまとめようと思ったのか
私自身、Ruby on Railsを学習し始めて約10ヶ月が経った段階ですが、なぜファットコントローラを避けるべきなのか、いまいち理解していません。笑
私が所属している会社でも、コード規約で、「なるべくモデル以上に寄せること」と定められているのですが、内心、「全部コントローラに書いた方がファイルを行き来しなくて良いから楽じゃない?」と思っています。笑
しかし、様々なところでファットコントローラという言葉を聞くので、やはりあまり良くないんだな〜と思い、なぜ良くないのかを理解し、腹落ちした上でコードを書きたいと思い、まとめることにしました!
ファットコントローラとは
まず、Ruby on Railsのファットコントローラについて調べてみました!
- 処理をコントローラ内で引き受けすぎてしまっていること
- コントローラ内のコードの量も、ロジックの量も増えていること
- コントローラ内にあらゆる実装を書き込んでいること
というように出てきました!
必要以上にコントローラ内のコードの量が多い状態のことみたいですね!
ChatGPTが考えるファットコントローラのイメージ画像
なぜファットコントローラを避けるべきなのか?
様々な記事を読んだところ、大きく分けると4つの理由があることが分かりました!
可読性
- ファットコントローラになると、どこにどの処理を書いたのかが分かりにくく、可読性が落ちてしまう
- 共通化すべきエラーハンドリングも各アクションごとに例外処理を実装すると、コードが冗長化してしまう
保守性
- ファットコントローラは、同じ処理を至る所に書いているケースが多く、改修漏れが発生しやすくなる
- 処理の一部のコードを修正すると、他の部分に影響が出てしまいやすくもなる
DRY原則
DRY原則とは、Don't Repeat Yourselfの略で、「ソフトウェア開発全体において情報を重複させない」という原則です!
ファットコントローラになってしまっていると、多くの場合、このDRY原則に反してしまっています!
MVCモデル
- MVCモデルとは、アプリケーションの設計を構造化するための一般的なアーキテクチャパターンです!
-
MVCは「Model-View-Controller」の略で、アプリケーションを3つの主要な部分に分割します!
-
Model
- データとビジネスロジックを扱う
- アプリケーションのデータ構造を定義し、データベースやファイルシステムとのやり取り、データの加工・処理を行う
-
View
- アプリケーションのユーザーが実際に見る画面やページのレイアウト、デザインを扱う
- モデルから受け取ったデータをもとに、ユーザーに表示するコンテンツを生成する
-
Controller
- ユーザーの入力とシステムの出力を仲介
- ユーザーからのアクションを受け取り、それに対する処理をモデルに指示し、その結果をビューに渡して反映させる
-
Model
また、このMVCモデルでは、
- Controllerは薄く保ち、ビジネスロジックはModelに切り出すこと
- ControllerはModelとViewの操作だけを記述し、肥大化を避けること
がお作法とされているみたいです!
私のコントローラのイメージはこうなりました!笑
たくさんの武器(モデル内のメソッドやロジック)を装備している戦士(コントローラ)
具体例
ChatGPTにファットコントローラの具体例のコードを書いてもらい、同時にファットコントローラの解消もお願いしてみました!
ファットコントローラの例
下記がChatGPTに書いてもらったファットコントローラのコード例です!
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
@comments = @post.comments.where(approved: true)
@related_posts = Post.where(category: @post.category).limit(3)
# 他にも多くのビュー関連の処理がここに含まれる可能性がある
end
def create
@post = Post.new(post_params)
@post.user_id = current_user.id
if @post.save
# 通知の送信、関連するモデルの更新、ログの記録など
send_notification_to_followers(@post)
update_user_statistics(current_user)
log_post_creation(@post)
redirect_to @post, notice: 'Post was successfully created.'
else
render :new
end
end
def update
@post = Post.find(params[:id])
if @post.update(post_params)
# 同じく、複数の後処理がここに含まれる可能性がある
update_user_statistics(current_user)
log_post_update(@post)
redirect_to @post, notice: 'Post was successfully updated.'
else
render :edit
end
end
private
def post_params
params.require(:post).permit(:title, :content, :category)
end
def send_notification_to_followers(post)
# フォロワーに通知を送信
end
def update_user_statistics(user)
# ユーザー統計の更新
end
def log_post_creation(post)
# 記事作成のログ記録
end
def log_post_update(post)
# 記事更新のログ記録
end
end
様々なメソッドやロジックがコントローラに書かれていて、コード量がかなり多いですね!
モデルにビジネスロジックを切り出し、ファットコントローラを解消した例
次に、ChatGPTに「モデルに切り出す方向で、このファットコントローラを解消して」とお願いしました!
class Post < ApplicationRecord
belongs_to :user
has_many :comments
# コールバックを使用してビジネスロジックを実行
after_create :send_notification_to_followers, :update_user_statistics, :log_post_creation
after_update :update_user_statistics, :log_post_update
private
def send_notification_to_followers
# フォロワーに通知を送信
end
def update_user_statistics
# ユーザー統計の更新
# self.user で Post の user にアクセスできる
end
def log_post_creation
# 記事作成のログ記録
end
def log_post_update
# 記事更新のログ記録
end
end
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
@comments = @post.comments.where(approved: true)
@related_posts = Post.where(category: @post.category).limit(3)
end
def create
@post = current_user.posts.build(post_params)
if @post.save
redirect_to @post, notice: 'Post was successfully created.'
else
render :new
end
end
def update
@post = Post.find(params[:id])
if @post.update(post_params)
redirect_to @post, notice: 'Post was successfully updated.'
else
render :edit
end
end
private
def post_params
params.require(:post).permit(:title, :content, :category)
end
end
修正後は、コントローラのコードがかなりスッキリしました!
モデルにメソッドやロジックを切り出し、コントローラをそれらを呼び出すものとして、きちんと役割分担されたコードになっています!
確かに、DRY原則やMVCモデルについて学び、コントローラの本来の役割の観点からコードを読むと、修正した方のコードの方が正しいような気がします!笑
さいごに
ファットコントローラとは何かというところから、避ける理由、具体例までをまとめてみましたが、理由なくファットコントローラがダメと言われているのではないことが分かりました!
これからは、きちんと腹落ちして、ファットコントローラを避けてコードを書くことが出来そうです!笑
最後まで読んでいただき、ありがとうございました!
参考文献
Discussion