Fat Controllrの解消をしてRails設計を学ぶ〜UseCaseクラス導入とその効果〜

## はじめに
個人開発でRailsアプリを作成する中で、コントローラーが肥大化していくことに悩んでいました。1つのアクションにロジックが詰め込まれ、「動くけど読みにくい」状態になっていました。この記事では、Fat Controllerをどう設計改善したか、UseCaseクラス導入のプロセスと学びをまとめます。

## Fat Controllerとは
- 処理をコントローラ内で引き受けすぎてしまっている
- コントローラ内のコードの量も、ロジックの量も増えている
- コントローラ内にあらゆる実装を書き込んでいる

必要以上にコントローラ内のコードがある状態をFatControllerと言います。

### Fat Controllerはなぜよくないのか
- コード量が増えすぎて、**可読性**が下がる
- ロジックが埋もれて、**再利用**しづらい
- テストがしにくく、**保守性**が悪化

Fat Controllerについては下記の記事がわかりやすく書いてありました。

https://zenn.dev/linkedge/articles/9e924c18e702d2

## 解消方法
ここで必要となってくるのが**単一責任の原則**と**DRY原則**という考え方です。
### 単一責任の原則（SRP: Single Responsibility Principle）
#### 定義
「ソフトウェアの各コンポーネント（クラス、モジュールなど）は、それぞれ1つの責任のみを持つべきだという原則」
つまり、1つの役割・目的だけを持つようにすることで、責務が明確になり、他のコードへの影響が最小限になる。

### DRY原則（Don't Repeat Yourself）
#### 定義
「同じロジック・知識を複数箇所に書かないこと」
同じコードを何度も書くことを避け、1か所にまとめて記述することで、保守性や可読性を向上させる。

## 実際のコード
### Before
```ruby 
#app/controllers/users_controller.rb
class UsersController < ApplicationController
  def update_password
    @user = current_user

    if @user.authenticate(params[:user][:current_password])
      if @user.update(params.require(:user).permit(:password, :password_confirmation))
        flash[:notice] = "パスワードを変更しました"
        redirect_to edit_password_user_path
      else
        flash.now[:alert] = "パスワードの更新に失敗しました"
        render :edit_password, status: :unprocessable_entity
      end
    else
      @user.errors.add(:current_password, "が正しくありません")
      flash.now[:alert] = "変更に失敗しました"
      render :edit_password, status: :unprocessable_entity
    end
  end
end
```
#### この書き方の問題点
- 認証・更新、エラー処理、画面遷移が1アクションに纏められている
- コントローラのコード量が増え、可読性が下がる
- テストもしづらく、変更時の影響範囲が広い

### After 
#### UseCaseクラスによる責務分散
```ruby
#app/controllers/users_controller.rb
def update_password
  service = UpdatePassword.new(
    current_user,
    current_password: params[:user][:current_password],
    new_params: password_params
  )

  if service.call
    flash[:notice] = "パスワードを変更しました"
    redirect_to edit_password_user_path
  else
    flash.now[:alert] = "変更に失敗しました"
    render :edit_password, status: :unprocessable_entity
  end
end

private

def password_params
  params.require(:user).permit(:password, :password_confirmation)
end
```

```ruby
#app/usecases/update_password.rb
class UpdatePassword
  def initialize(user, current_password:, new_params:)
    @user = user
    @current_password = current_password
    @new_params = new_params
  end

  def call
    if @user.authenticate(@current_password)
      @user.update(@new_params)
    else
      @user.errors.add(:current_password, "が正しくありません")
      false
    end
  end
end
```



## 設計改善ポイント
### 単一責任の原則（SRP）を守れる
以前のFat Controllerでは、1つのメソッドが「認証」「更新」「フラッシュメッセージの出力」「画面遷移」など、複数の責務を抱えていました。

UseCaseを導入したことで、認証・更新処理はUseCaseに委譲
フラッシュや画面遷移はControllerに限定と、役割を明確に分けられ、どこを変更すればよいかもはっきりしました。

### Fat Controller を避けられる
責務の分離により、コントローラーの処理が短く、読みやすくなりました。update_password の中身が「処理を呼び出す」だけになったことで、流れが明確に把握でき、保守性が格段に上がりました。

### DRY原則の実現
同じような処理（バリデーションやパラメータ処理）が複数箇所に分散していると、修正漏れやバグの温床になります。
UseCase導入によって、重複しやすいロジック（認証や更新）を1か所にまとめられたことで、変更時の影響範囲を最小限にできました。

### テストがしやすくなる
UseCaseクラスはコントローラーから切り離されているため、RSpecでの単体テストが可能になりました。
たとえば、認証失敗や更新失敗のケースを明示的にテストできます。

## 得られた学び
今回の改善を通して、設計や保守性に対する考え方が大きく変わりました。

まず、Fat Controllerがなぜ問題なのかを実感しました。認証や更新、フラッシュメッセージの制御といった処理を1つのアクションに詰め込むことで、コードの見通しが悪くなり、バグの温床になりやすいことを痛感しました。

そこでUseCaseクラスを導入することで、パスワード変更という単一の責任を切り出し、コントローラーでは処理の流れだけに集中できるようになりました。結果として、単一責任の原則（SRP）を意識した保守しやすい設計へと改善されました。

また、重複しやすいロジックをUseCaseに集約したことで、DRY原則（Don't Repeat Yourself）も実践できました。1か所を修正すれば全体が改善される設計になり、将来的な仕様変更にも柔軟に対応できます。

さらに、UseCaseは外部依存が少なく、RSpecでの単体テストが非常にしやすくなりました。認証失敗やバリデーション失敗といったシナリオを分離して検証できることで、品質保証にもつながりました。

コードの可読性、保守性、再利用性、テストのしやすさという点で、UseCaseパターンは非常に有効であると体感しました。

「とりあえず動く」から一歩進み、コードの役割を明確に分離する設計を目指しました。UseCaseクラスによってコードの見通しが良くなり、保守や機能追加のしやすさにもつながっています。