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?

ex-crowdworksAdvent Calendar 2024

Day 17

〜データ取得ロジックが肥大化するの厳しいって〜Railsで複雑なロジックを整理するためのQueryオブジェクトパターンを実例付きで解説!

Last updated at Posted at 2024-12-16

この記事は ex-crowdworks Advent Calendar 2024の17日目の記事です。

はじめに

今年、株式会社クラウドワークスを退職した@nisyuuです。プロテインはsavasを飲むことが多いです。
エンジニアとしてクラウドワークステック(旧クラウドテック)というフリーランスと企業をマッチングするエージェントサービスを開発していました。

Webアプリケーションを開発する中でデータの取得ロジックが複雑化しメソッドが冗長になってしまうケースがよくあります。
このような問題を解決するために役立つのが「Queryオブジェクトパターン」です。

本記事では初心者向けにQueryオブジェクトパターンの概要とRailsにおける実装方法を解説します。

Queryオブジェクトパターンとは

Queryオブジェクトパターンは、アプリケーション内で特定のデータを取得するロジックをオブジェクトに切り出すデザインパターンです。
データベースクエリの責務を分離し、コードの可読性や再利用性を高めることを目的としています。このパターンは、特に複雑なクエリや複数の条件が必要な場合に有用です。

基本的な考え方

  • クエリを専用のクラスに分離する
  • 名前付きメソッドでクエリを定義する
  • コントローラやモデル内のロジックを簡潔に保つ

Queryオブジェクトパターンのメリット

  • 再利用性の向上
    • 同じクエリを複数の場所で簡単に利用可能
  • テストが容易
    • クエリロジックを単体テストできる
  • 責務の分離
    • コントローラやモデルからクエリロジックを切り離すことで、コードがシンプルに
  • 可読性の向上
    • クエリがオブジェクトにカプセル化され、意図が明確になる

Queryオブジェクトパターンのデメリット

  • クラスの増加
    • 小規模なアプリケーションではクラスが増えすぎる可能性
  • 初期学習コスト
    • パターンを理解するための時間が必要
  • 過剰設計のリスク
    • シンプルなクエリに適用すると、かえって複雑になることも

RailsでのQueryオブジェクトパターン実装例

例: 投稿の検索機能

以下は、投稿を特定の条件でフィルタリングするQueryオブジェクトの例です。

ステップ1: Queryオブジェクトの作成

app/queries/posts_query.rb:

class PostsQuery
  def initialize(relation = Post.all)
    @relation = relation
  end

  def with_title(title)
    @relation = @relation.where("title LIKE ?", "%#{title}%") if title.present?
    self
  end

  def by_author(author_id)
    @relation = @relation.where(author_id: author_id) if author_id.present?
    self
  end

  def recent
    @relation = @relation.order(created_at: :desc)
    self
  end

  def result
    @relation
  end
end

ステップ2: コントローラでの利用

app/controllers/posts_controller.rb:

class PostsController < ApplicationController
  def index
    query = PostsQuery.new
                  .with_title(params[:title])
                  .by_author(params[:author_id])
                  .recent

    @posts = query.result
  end
end

ステップ3: テスト

spec/queries/posts_query_spec.rb:

require "rails_helper"

RSpec.describe PostsQuery, type: :model do
  let!(:post1) { create(:post, title: "First Post", author_id: 1, created_at: 1.day.ago) }
  let!(:post2) { create(:post, title: "Second Post", author_id: 2, created_at: 2.days.ago) }

  it "filters posts by title" do
    result = PostsQuery.new.with_title("First").result
    expect(result).to eq([post1])
  end

  it "filters posts by author" do
    result = PostsQuery.new.by_author(2).result
    expect(result).to eq([post2])
  end

  it "orders posts by recent" do
    result = PostsQuery.new.recent.result
    expect(result).to eq([post1, post2])
  end
end

おわりに

Queryオブジェクトパターンは、複雑なクエリロジックを整理し、コードの再利用性やテストの容易性を高める強力な手法です。
ビジネス要件によっては、データの取得方法が複雑化することもありますが、Queryオブジェクトパターンを活用することでロジックを整理しながら実装が進められます。

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?