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

Railsでファットモデル・ファットコントローラーを避ける方法(Concern)

0
Posted at

はじめに

Railsで開発をしていると、共通の処理を複数のModelやControllerへコピー&ペーストしてしまい、「同じようなコードがあちこちにある」という状況になることがあります。

そんなときに役立つのがConcernです。

この記事では、ファットモデル・ファットコントローラーを避けるための代表的な設計パターンの一つであるConcernについて整理してみます。

Concernとは

Concernは、Rails標準の ActiveSupport::Concern を利用して、ModelやControllerで共通の処理をまとめるための仕組みです。

共通のメソッドをまとめるだけであればRuby標準のModuleでも実現できますが、extend ActiveSupport::Concern を利用することで、included do や class_methods do を使いながら共通の設定やメソッドを分かりやすく定義できます。

複数のControllerで共通して利用する before_action やprivateメソッド、複数のModelで共通するスコープやバリデーション、メソッドなどを切り出す用途で利用されます。

この記事では、ファットモデル化を防ぐ手段の一つとして利用されるModel Concernを例に紹介します。

例えば、Article と News のように、複数のModelが同じ「公開・非公開」の振る舞いを持つ場合、共通の処理をConcernとして切り出すことができます。

実装例

# app/models/concerns/publishable.rb
module Publishable
  extend ActiveSupport::Concern

  included do
    scope :published, -> { where(published: true) }

    validates :published, inclusion: { in: [true, false] }
  end

  class_methods do
    def recent
      published.order(created_at: :desc)
    end
  end

  def publish!
    update!(published: true)
  end
end

Concernを利用したいModelでは、include Publishable を書くだけで、公開機能に関する共通のスコープやバリデーション、メソッドを利用できます。

# app/models/article.rb
class Article < ApplicationRecord
  include Publishable
end

# app/models/news.rb
class News < ApplicationRecord
  include Publishable
end

これにより、Article と News のどちらでも同じ機能を利用できます。

Article.published
News.published

Article.recent
News.recent

article.publish!
news.publish!

Concernは便利ですが、「とりあえず共通処理をConcernへ移す」という使い方をすると、コードの見通しが悪くなったり、責務が分かりづらくなる可能性もあります。

そのため、

  • 本当に複数のクラスで再利用される処理なのか
  • 共通の責務と言えるのか

を意識して切り出した方が、後から読みやすいコードになりやすいと思います。

その他の設計パターン

Railsでは、ファットモデルやファットコントローラーを避けるために、Concern以外にもさまざまな設計パターンがあります。

パターン 主な用途
Service Object ユースケース単位の業務処理をまとめたいとき
Concern 複数のModelやControllerで共通の責務を切り出したいとき
Form Object フォーム入力や複数モデルのバリデーション・保存をまとめたいとき
Query Object 検索条件やJOINが複雑になり、scopeだけでは見通しが悪くなったとき
Policy Object 認可ロジックを分離したいとき
Decorator / Presenter View表示用のロジックをModelから切り離したいとき
PORO(Plain Old Ruby Object) ActiveRecordを継承しない通常のRubyクラスとして、ドメインロジックを切り出したいとき

今回はConcernに絞って紹介しましたが、他の設計パターンについても、機会があれば整理して記事にしてみたいと思います。

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