1
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?

はじめに

こんにちは。アメリカ在住で独学エンジニアを目指している Taira です。

前回の Query Object に続くリファクタリングシリーズです。
Rails の API 開発では、モデルの値をそのまま返すのではなく、

  • 整形(通貨形式、日付形式など)
  • ラベル化(enum → 表示文字列)
  • ネスト構造化(関連モデルを JSON に含める)
    が必要になる場面が多くあります。

これを各 Controller や View に散らばせると、レスポンス仕様の所在が不明確になり、保守性が下がります。

そこで使えるのがPresenterです。


Presenter とは?

Presenterは、

ドメインオブジェクト(モデル)の生データを、API レスポンスに適した形式へ変換する“変換レイヤー”

です。

MVC の中では以下のような位置づけになります。

Model    → 生データ(ドメイン)
Presenter→ 出力用データ(変換・整形)
View/API → 最終的な描画や送信
  • Model:ビジネスロジック・データ構造
  • Presenter:表示や出力に適した形へ変換
  • API:変換後のデータを返却

実装例

以下は PORO(Plain Old Ruby Object) といい、ActiveRecord などを継承しない Ruby のオブジェクトとして書いています。

Presenter

# app/presenters/teachers/index_presenter.rb
class Teachers::IndexPresenter
  def initialize(result)
    @current = result[:current]
    @teachers = result[:teachers]
  end

  def as_json(*)
    {
      current_user: format_user(@current),
      teachers: @teachers.map { |teacher| format_user(teacher) }
    }
  end

  private

  def format_user(user)
    user.as_json(
      include: {
        students: {
          only: %i[id student_code name status school_stage grade]
        },
        teaching_assignments: {
          only: %i[id student_id user_id teaching_status]
        },
        class_subjects: {
          only: %i[id name]
        },
        available_days: {
          only: %i[id name]
        }
      },
      methods: %i[last_sign_in_at current_sign_in_at]
    )
  end
end

Controller での使い方

# app/controllers/api/v1/teachers_controller.rb
class Api::V1::TeachersController < ApplicationController

  def index
    result = Teachers::IndexQuery.call(current_user, school: @school)
    render json: Teachers::IndexPresenter.new(result).as_json, status: :ok
  end
end


メリット

  • レスポンス仕様を 1 か所に集約
  • Controller がスリムになる
  • Presenter は純 Ruby クラスなので単体テストが容易
  • Jbuilder や Serializer を使わない軽量な実装が可能

他の手段との比較

手法 特徴
Presenter 純 Ruby クラス。依存を増やさず軽量に出力仕様を定義できる
Jbuilder / RABL テンプレート形式で定義。ビュー層寄り
ActiveModel::Serializer gem ベースで柔軟だが依存が増える

まとめ

  • Presenter は「モデルの生データを API レスポンス用に変換する責務」を持つ
  • Controller でPresenter.new(model)を返すだけにすると仕様の集約が容易
  • 単体テストで仕様を保証しやすく、保守性が高まる

💡 ポイント
「このエンドポイントのレスポンスはどこで定義しているの?」という質問に、
**「この Presenter です」**と即答できる状態を作ると、チーム開発でも強いです。

1
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
1
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?