7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Fat Controllerは何がダメ?Rails初学者が学んだ設計の基本

7
Posted at

はじめに

現在、プログラミングスクールでRuby on Rails を学習中です。

カリキュラムの課題をクリアしたあとに、自分の書いたコードと模範解答を見比べてみたところ、あることに気づきました。

自分のコントローラーは長い。
模範解答はスッキリしている。

処理の内容は同じなのに、なぜこんなにも違うのか。

そこで出てきたのが「Fat Controller(太ったコントローラー)」という考え方でした。

この記事では、

  • Fat Controllerとは
  • なぜ問題なのか
  • どう改善すればよいのか

を、Rails初学者の視点で整理してみます。

Fat Controllerとは

Railsは MVC(Model-View-Controller) という設計パターンを採用しています。

それぞれの役割はざっくり言うと次の通りです。

役割
Model データやビジネスロジック
View 表示
Controller リクエストを受け取り、処理を振り分ける

Controllerは本来、「交通整理役」です。

しかし、Controllerに

  • 複雑な条件分岐
  • データ加工処理
  • 外部API連携
  • ビジネスロジック
  • メール送信処理

などをどんどん書いていくと、ファイルは肥大化していきます。

これが Fat Controller と呼ばれる状態です。

Rails界隈では

Fat Model, Skinny Controller

という言葉もあります。

つまり「Controllerはできるだけ薄く保とう」という考え方です。

なにが問題なのか

Fat Controllerの問題は、単に「コードが長いこと」ではありません。
本質は、責務が集中していることです。

① 単一責任の原則に違反する

Controllerが

  • ビジネスルール
  • データ保存
  • 条件分岐
  • 外部連携

などを同時に担うと、

仕様変更があるたびにControllerを修正することになり、壊れやすいコードになります。

② テストがしづらい

Controllerにロジックを書くと、テストはHTTPリクエスト前提になります。

処理の中身だけを確認したい場合も、
HTTPリクエストを通してテストする必要があります。

ロジックをModelやServiceに切り出せば、単体テストが可能になります。

③ 再利用できない

Controllerに書いたロジックは、Webリクエストに強く依存します。

  • バッチ処理で使いたい
  • 別APIからも使いたい
  • コンソールから呼び出したい

といった場合に再利用しづらくなります。

④ 変更に弱い

責務が集中していると影響範囲も広がります。

結果として、

  • コンフリクトが増える
  • バグが混入しやすい
  • 可読性が下がる

といった問題が発生します。

どう改善するか

では実際に、記事投稿アプリの例で見てみます。

想定機能

  • ユーザーが記事を投稿する
  • 文字数が100文字以上なら「公開(published)」にする
  • 100文字未満なら「下書き(draft)」にする
  • 公開された場合は通知メールを送る

Fat Controllerの例

def create
  @post = Post.new(post_params)

  # 文字数によってステータスを決定
  if @post.content.length >= 100
    @post.status = "published"
  else
    @post.status = "draft"
  end

  if @post.save
    # 公開された場合のみ通知メール送信
    if @post.status == "published"
      NotificationMailer.post_published(@post).deliver_now
    end

    redirect_to @post
  else
    render :new
  end
end

このControllerは、

  • 文字数判定ロジック

  • ステータス決定

  • メール送信処理

  • 保存処理

  • 画面遷移制御

をすべて担当しています。
責務が集中している状態です。

改善例①(Modelにロジックを移す)

まずはビジネスロジックをModelに移してみます。

# app/models/post.rb
class Post < ApplicationRecord
  def assign_status
    self.status = content.length >= 100 ? "published" : "draft"
  end

  def publish_notification
    return unless status == "published"

    NotificationMailer.post_published(self).deliver_now
  end
end

Controllerは次のようになります。

def create
  @post = Post.new(post_params)
  @post.assign_status

  if @post.save
    @post.publish_notification
    redirect_to @post
  else
    render :new
  end
end

Controllerが少しスッキリしました!

改善例②(Serviceオブジェクトに切り出す)

さらに責務を整理して、投稿処理全体をServiceにまとめます。

# app/services/post_creator.rb
class PostCreator
  def self.call(params)
    post = Post.new(params)
    post.assign_status

    if post.save && post.status == "published"
      NotificationMailer.post_published(post).deliver_now
    end

    post
  end
end

controllerはこうなります。

def create
  @post = PostCreator.call(post_params)

  if @post.persisted?
    redirect_to @post
  else
    render :new
  end
end

めちゃくちゃスッキリしました!

改善後のポイント

Controllerは、

  • パラメータを受け取る

  • 処理を呼び出す

  • レスポンスを決める

だけになりました。

ビジネスロジックはControllerから分離され、
テストや再利用もしやすい構造になります。

これが「Skinny Controller」に近い形です。

まとめ

今回の学びは、

「動くコード」と「設計されたコード」は違う

ということでした。

最初は「とりあえず動けばOK」と思いながら、
ほとんどの処理をControllerに書いていました。

しかし、

  • 責務を分ける

  • ロジックを適切な場所に置く

  • Controllerは薄く保つ

という考え方を知ってから、コードの見え方が大きく変わりました。

まだまだ難しい部分はありますが、意識していきたいと思いました。

参考記事

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?