1. ホーム
  2. ruby-on-rails

[解決済み] railsアプリケーションでapp/services/はどのように使われているのでしょうか?

2022-03-03 10:23:37

質問

Ruby on Railsのエコシステムで、時々このようなことに出くわします。

class LocalizeUrlService
class Services::UpdateUserRegistrationForOrder
class ProductionOrderEmailService
UserCart::PromotionsService.new(
Shipping::BulkTrackingService.new(bulk_update, current_spree_user)

また、例として ここで

しかし、例えば "Ruby On Rails Guides" などの公式の例では、このような例は見たことがありません。これは、Rails/OOPとは異なる別の言語/パラダイムから来た概念であると思われます。

このパラダイム/トレンドはどこから来ているのでしょうか?チュートリアルや本があるのでしょうか? この人たちは影響を受けたのでしょうか?この人たちは、数年前のSOAトレンドの生き残りなのでしょうか?

app/service/blah_service.rb にコードを置くのは良いアイデアですか? もしそうなら、どのようなロジックやコードがサービス材料とみなされるのでしょうか。 もしそうなら、どのようなロジック/コードが "Service"材料とみなされるのでしょうか?

app/servicesフォルダを作成するgem/pluginは何ですか?vanilla railsのアプリにはまず入っていません。

sidetoneです。個人的には、サービスをインスタンス化することに問題があります。クラスとインスタンス化は、多くのプログラマが間違って使っているように思います。クラスとインスタンス化は、quot;物のためであり、サービスはquot;するものであると思います。 ということで、mixin/defs/includeがいいんじゃないかと思います。

解決方法は?

サービスオブジェクトは、通常のMVCパラダイムにうまく当てはまらないもののためにあります。サービスオブジェクトは、通常、モデルやコントローラが肥大化してしまうようなビジネスロジックに使用されます。一般的に、サービスオブジェクトは状態を持たず(モデル内に保持されます)、APIや他のビジネスロジックと会話するようなことをします。サービスオブジェクトは、モデルを薄くし、集中させることができます。そして、それぞれのサービスオブジェクトも薄く、一つのことに集中します。

Railsサービスオブジェクト。包括的なガイド は、Twitterとの会話を管理するためにサービスオブジェクトを使用する例や、複数のモデルを横断するような複雑なデータベーストランザクションをカプセル化する例などがあります。

Ruby on Railsのサービスオブジェクト...そしてあなたへ は、新規ユーザー登録処理を管理するためのサービスオブジェクトを作成することを示しています。

EngineYardのブログで サービスを使ってRailsのコントローラをクリーンでDRYに保つ クレジットカード決済を行うサービスオブジェクトの例です。

原点に立ち返るなら Railsのサービスオブジェクトは、クリーンで保守性の高いコードを設計するのに役立ちます。その方法を紹介します。 は、登場したばかりの2014年のものです。

<ブロッククオート

サービスには、アプリケーションのコアロジックをコントローラやモデルに分散させることなく、別のオブジェクトに集中させることができるという利点があります。

すべてのサービスに共通する特徴は、そのライフサイクルにあります。

  • 入力を受け付ける
  • 作業を行う
  • 結果報告

もしこれが、関数が行うことに酷似していると思われるなら、その通りです。彼らはさらに call をサービス上のパブリックメソッド名として使用します。 プロック . サービスオブジェクトは、大きなサブルーチンになってしまうものに名前を付けて整理する方法と考えることができます。

Railsサービスオブジェクトの構造 は、サービスオブジェクトと懸念の違いを取り上げています。また、サービスオブジェクトがモジュールよりも優れている点についても説明します。また、良いサービスオブジェクトとはどのようなものかを詳しく説明しています。

<ブロッククオート
  • 状態を保存しない
  • クラスメソッドではなく、インスタンスメソッドを使用する
  • パブリックメソッドはほとんどないほうがいい
  • メソッドのパラメータは、操作されるか入力として必要とされる値オブジェクトであるべきです。
  • メソッドは、ブール値ではなく、リッチな結果オブジェクトを返す必要があります。
  • 依存するサービスオブジェクトは、プライベートメソッドでアクセスできるようにし、コンストラクタ内または遅延して作成する必要があります。

例えば、ユーザーをリストに登録するアプリケーションがあったとして、それは3つのモデルになるかもしれません。User、List、Subscriptionです。

class List
  has_many :subscriptions
  has_many :users, through: :subscriptions  
end

class User
  has_many :subscriptions
  has_many :lists, through: :subscriptions
end

class Subscription
  belongs_to :user
  belongs_to :list
end

リストへのユーザーの追加と削除の処理は、基本的な createdestroy メソッドとアソシエーション、そしていくつかのコールバックがあります。

今、あなたの上司は、大規模なロギング、統計情報の追跡、SlackやTwitterへの通知、メールの送信、大規模な検証を行う、手の込んだ購読プロセスを求めています...今まではシンプルな createdestroy は、APIにコンタクトし、複数のモデルを更新する複雑なワークフローになります。

それらをすべて懸念事項やモジュールとして記述し、これら3つの元単純なモデルにそれらすべてを含めて、大きな Subscription.registerSubscription.remove というクラスメソッドがあります。これでサブスクリプションは、ツイートやSlackへの投稿、メールアドレスの確認、バックグラウンドチェックができるようになりましたね。変ですね。あなたのモデルは、コア機能とは関係のないコードで肥大化しています。

代わりに、次のように書きます。 SubscriptionRegistrationSubscriptionRemove サービスオブジェクトです。これらには、統計情報をツイートして保存したり、バックグラウンドチェックを実行したりする機能などを含めることができます(というより、それをより多くのサービスオブジェクトに入れる可能性が高いです)。それぞれ1つのパブリックメソッドを持っています。 SubscriptionRegistration.perform(user, list)SubscriptionRemove.perform(subscription) . ユーザー、リスト、サブスクリプションは何も知らなくていい。あなたのモデルはスリムなまま、ひとつのことをするだけです。そして、あなたのサービスオブジェクトはそれぞれ1つのことをします。

具体的な質問ですが...

このパラダイム/トレンドはどこから来ているのか?

私が知る限り、これは「ファットモデル/スキニーコントローラー」というトレンドの帰結です。それは良いアイデアなのですが、しばしばモデルが太りすぎてしまうのです。モジュールや関係性を考慮しても、1つのクラスに詰め込むには多すぎるのです。通常モデルやコントローラを肥大化させる他のビジネスロジックは、サービスオブジェクトに入ります。

<ブロッククオート

app/servicesフォルダを作成するgem/pluginは何ですか?

するんですね。にあるものはすべて app/ はRails 5でオートロードされます。