🤖

Rubyで学ぶリポジトリパターン (Repository Pattern)

2025/01/25に公開

1. どんなもの?

リポジトリパターンは、ドメインロジックとデータアクセスロジックを分離するためのデザインパターンです。
データベースへのクエリを直接記述するのではなく、「リポジトリ」と呼ばれるクラスを通じて、データの取得や保存を行います。

例えば、複数のデータソース(SQLデータベース、API、キャッシュなど)を統一されたインターフェースで扱う場合に役立ちます。

2. 通常の実装方法と比べてどこがすごいの?

通常の方法

データベース操作を直接モデルやコントローラー内に記述する場合、以下の課題が発生します。

class NotificationManager
  def initialize(users)
    @users = users
  end

  def send_notifications
    # データアクセスロジック
    active_users = @users.select { |user| user[:active] }.sort_by { |user| user[:created_at] }.reverse

    # ビジネスロジック
    active_users.each do |user|
      puts "Sending notification to #{user[:name]}"
    end
  end
end

users = [
  { name: "Alice", active: true, created_at: Time.now - 3600 },
  { name: "Bob", active: false, created_at: Time.now - 7200 },
  { name: "Carol", active: true, created_at: Time.now - 1800 }
]

manager = NotificationManager.new(users)
manager.send_notifications
  • 課題:
    • データアクセスロジックとビジネスロジックが混在しコードが読みにくく、変更に強くない。
    • データソースの変更が難しい(例えばハッシュからSQLデータベースに変更する場合など)。

リポジトリパターンの利点

リポジトリパターンを使うと、データ操作をリポジトリクラスに委譲できるため、コードの分離と再利用性が向上します。

# データアクセスロジック
class UserRepository
  def initialize(users)
    @users = users
  end

  def active_users
    @users.select { |user| user[:active] }.sort_by { |user| user[:created_at] }.reverse
  end
end

# ビジネスロジック
class NotificationManager
  def initialize(repository)
    @repository = repository
  end

  def send_notifications
    active_users = @repository.active_users
    active_users.each do |user|
      puts "Sending notification to #{user[:name]}"
    end
  end
end

users = [
  { name: "Alice", active: true, created_at: Time.now - 3600 },
  { name: "Bob", active: false, created_at: Time.now - 7200 },
  { name: "Carol", active: true, created_at: Time.now - 1800 }
]

repository = UserRepository.new(users)
manager = NotificationManager.new(repository)
manager.send_notifications
  • 利点:
    • データアクセスロジックをリポジトリに集中させることで、コードの可読性と再利用性が向上。
      • データアクセスロジック(UserRepository)とビジネスロジック(NotificationManager)が分離され、役割が明確になる。
    • データソースが変更されても、リポジトリ内で対応できるため、他のコードへの影響を最小化。
      • 通知方法を変更する場合も NotificationManager のみを修正すれば良いので、コードの可読性と保守性が向上する。

3. 技術や手法の"キモ"はどこにある?

  1. 統一されたインターフェース

    • リポジトリは、データソースに依存しない統一されたインターフェースを提供します。
  2. 単一責任の原則(SRP)

    • リポジトリはデータ操作に特化し、クラスからデータアクセスロジックを分離します。
  3. 依存性注入

    • リポジトリを注入することで、異なるデータソースや条件に応じた実装を切り替え可能にします。

4. 実装例

例: JSONデータソースへの切り替え

データソースをハッシュからJSONファイルに切り替える例です。

require 'json'

class JsonUserRepository
  def initialize(json_file_path)
    @json_file_path = json_file_path
  end

  def active_users
    users = JSON.parse(File.read(@json_file_path), symbolize_names: true)
    users.select { |user| user[:active] }.sort_by { |user| user[:created_at] }.reverse
  end
end

json_repo = JsonUserRepository.new("users.json")
puts json_repo.active_users
  • 実装ポイント
    • データソースをJSONファイルに変更する場合、別のリポジトリを用意するだけでよく、他のクラスへの影響はない。
Railsで実装するとしたら...

Rails では ActiveRecord があるので、あまりリポジトリパターンを使う必要はありませんが、以下のように実装できます。

例えば、リポジトリを使って、アクティブユーザーを取得するコードを統一します。

class UserRepository
  def initialize(scope = User)
    @scope = scope
  end

  def active_users
    @scope.where(active: true).order(created_at: :desc)
  end
end

class UsersController < ApplicationController
  def index
    repository = UserRepository.new
    @users = repository.active_users
  end
end
  • 実装ポイント:
    • データソースが変更されても、リポジトリ内で対応できるため、コントローラーへの影響を最小化。

この例では ActiveRecord を使っていますが、他のデータソース(API、キャッシュなど)を使っている場合にリポジトリパターンが有効です。

5. 議論はあるか?

メリット

  • データアクセスロジックを分離することで、コードの可読性と再利用性が向上。
  • データソースが変更されても、リポジトリ内で対応可能。
  • テストが容易になる(リポジトリのモックを利用可能)。

デメリット

  • リポジトリクラスが増えると管理が煩雑になる。
  • 簡単なプロジェクトではオーバーヘッドとなる可能性がある。

議論

リポジトリパターンは、複雑なデータアクセスロジックが必要なプロジェクトで非常に有効ですが、シンプルなプロジェクトでは過剰設計となる場合があります。適切な範囲で利用することが重要です。


6. まとめ

リポジトリパターンは、データアクセスロジックを分離し、コードの可読性と拡張性を高めるデザインパターンです。

このパターンを適切に活用することで、柔軟で保守性の高いアプリケーション設計が可能になりますが、過剰設計にならないよう注意する必要があります。

GitHubで編集を提案

Discussion