🔥

RailsにおけるCQRSパターンの採用: FatModel・FatController問題の解決策

2024/03/23に公開1

2023年のKaigi on RailsでCQRSについての発表があり、とても勉強になったのでCQRSについてまとめました。

Railsアプリケーションの開発において、FatModelやFatControllerは頻繁に直面する問題ですよね。
これらの問題は、アプリケーションの拡張性や保守性を低下させ、複雑性を増加させます。
CQRS(Command Query Responsibility Segregation:コマンドクエリ責任分離)パターンの採用により、これらの問題を効果的に解決することができます。
本記事では、RailsアプリケーションにCQRSを採用するメリットと、サンプルコードを通じてその実装方法を解説します。

CQRSパターンとは?

CQRSパターンは、アプリケーションの操作を「コマンド(データの変更)」と「クエリ(データの読み取り)」の2つに分離するアーキテクチャパターンです。これにより、アプリケーションの読み書きの責任が明確に分離され、それぞれの処理の最適化が可能になります。

CQRSのメリット

  • 明確な責任分離: データの読み取りと書き込みの責任がはっきりと分離されるため、コードの可読性と保守性が向上します。
  • 拡張性の向上: 読み取りと書き込みの処理が独立しているため、将来的な機能拡張やパフォーマンスの最適化が容易になります。
  • 複雑性の管理: ビジネスロジックが分離されることで、システムの複雑性を効果的に管理できます。特に、読み取り専用のクエリでは、高度なクエリ最適化やキャッシュ戦略が容易に適用できます。
  • テストの容易性: 責務が分離されているため、単体テストや統合テストが書きやすくなります。

FatModel・FatController問題とCQRS

Railsでは、ビジネスロジックやデータアクセスロジックがモデルやコントローラに集中しやすい傾向があります(FatModel、FatController)。
これにより、コードが肥大化し、テストや保守が困難になります。CQRSを採用することで、データの変更と読み取りを分離し、これらの問題を解決できます。

CQRSの実装サンプル

CQRSパターンをRailsアプリケーションに適用するための基本的なステップとサンプルコードを以下に示します。

コマンド(データ変更)

コマンドはデータを変更するための操作です。例)ユーザーの情報を更新する操作

# app/commands/update_user_command.rb
class UpdateUserCommand
  def initialize(user, user_params)
    @user = user
    @user_params = user_params
  end

  def call
    @user.update(@user_params)
  end
end

コントローラでのコマンドの使用

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def update
    user = User.find(params[:id])
    command = UpdateUserCommand.new(user, user_params)
    if command.call
      redirect_to user_path(user), notice: 'User was successfully updated.'
    else
      render :edit
    end
  end

  private

  def user_params
    params.require(:user).permit(:name, :email)
  end
end

クエリ(データ読み取り)

クエリはデータを読み取るための操作です。例)特定の条件に合致するユーザーを検索する操作

# app/queries/users_query.rb
class UsersQuery
  def initialize(scope = User.all)
    @scope = scope
  end

  def call(params)
    @scope.where(params)
  end
end

コントローラでのクエリの使用

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    query = UsersQuery.new
    @users = query.call(name: params[:name])
  end
end

注意点

CQRSの導入は、アプリケーションの構造を複雑にする可能性があります。小規模なプロジェクトや単純なアプリケーションでは、導入によるメリットが得られない場合もあります。CQRSの導入を検討する際は、アプリケーションの規模や複雑さ、将来的な成長予測を総合的に考慮することが重要です。

まとめ

RailsアプリケーションでのCQRSパターンの採用は、FatModelやFatControllerといった問題を解決し、よりクリーンで保守性の高いコードベースを実現するための強力な手段です。
しかし、その導入はプロジェクトの規模やニーズに応じて慎重に行う必要があります。適切に実装されたCQRSは、アプリケーションの拡張性、保守性、およびパフォーマンスを大きく向上させることができます。

Discussion

Terai ShogoTerai Shogo

よくまとまっていてすごく良い記事だと思いました!
発表した後に自分が誤っていたことに気づいたのですが、参照用のUserモデルと更新用のUserモデルを分離しない場合(自分の発表の場合)は、CQRSではなくCQSと表現するのが正しいようだったので共有させていただきます!
https://tech.youtrust.co.jp/entry/2023/10/31/175100