パックワークによるRuby on Railsのモジュール化 エピソード1
人間は、多くの時間と労力を割かなければ、問題の全体像を把握することが難しい。これは特に、大規模で複雑なアプリケーションを扱っているときに起こる......。
Railsを使っているときによくある問題は、機能のロジックをどこに配置するかということです。
ロジックは多くの場合、コントローラやモデル、あるいは運が良ければサービスオブジェクトに配置される。では、サービスオブジェクトがあるのなら、なぜユースケースが必要なのでしょうか?
この記事で、このパターンの利点を発見しよう。
ユースケースとは、ゴールを達成するための、役割とシステム間の相互作用を定義する、アクションまたはイベントステップのリストである。
このパターンがさまざまな形で応用され、別の呼び名もあることは特筆に値する。例えば インターアクター, オペレーター または コマンドしかし ルビー コミュニティ ユースケース.すべての実装は異なるが、目的は同じである。
たとえ プロジェクト を使って要件を定義しているわけではありません。 ユースケースこのパターンは、ビジネス・ロジックを実用的な方法で構造化するのに役立つ。
私たちの 使用例 でなければならない:
私たちのシステムで何かを購入したいユーザーを例にとってみましょう。
モジュール UseCases
モジュール Buyer
クラス Purchase
def initialize(buyer:, cart:)
買い手 = 買い手
カート = カート
終了
def call
return unless check_stock
return unless create_purchase
通知 end
プライベート
attr_reader :buyer, :cart
def check_stock
Services::CheckStock.call(cart: cart)
終了
def create_purchase
Services::CreatePurchase.call(buyer: バイヤー, cart: カート).call
終了
def notify
Services::NotifyBuyer.call(buyer: バイヤー)
end
終了
終了
終了
ご覧のように コード 例として ユースケース Purchaseと呼ばれます。パブリック・メソッドは コール.callメソッドの内部には、購入を行うための基本的なステップがあり、すべてのステップはprivateメソッドとして定義されています。すべてのステップはサービスオブジェクトを呼び出しています。 ユースケース は購入を行うためのステップを定義しているだけで、ロジックそのものを定義しているわけではありません。これにより、私たちのシステムで何ができるのか(購入をする)、そしてそれを達成するためのステップが明確になります。
これで、最初の ユースケース コントローラーから
クラス コントローラ
購入
UseCases::Buyer::Purchase.new(
buyer: purchase_params[:buyer]、
cart: purchase_params[:cart].
).call
...
終了
...
終わり
この観点から ユースケース はサービス・オブジェクトによく似ているが、目的は異なる。サービス・オブジェクトは低レベルのタスクを実行し、データベースのようなシステムのさまざまな部分と相互作用します。 ユースケースの作成 新しいハイレベルの抽象化であり、論理的なステップを定義している。
最初の ユースケース は機能しているが、もっと良くなるかもしれない。どうすれば改善できるだろうか?を活用しよう。 ドライ 宝石。今回は ドライトランザクション.
まず、ベース・クラスを定義しよう。
クラス UseCase
include Dry::Transaction
クラス << self
def call(**args)
new.call(**args)
終了
end
エンド
これにより、UseCaseトランザクションに属性を渡して使用することができるようになります。これでPurchaseユースケースを再定義する準備が整いました。
モジュール UseCases
モジュール Buyer
クラス Purchase
def initialize(buyer:, cart:)
買い手 = 買い手
カート = カート
終了
def call
return unless check_stock
return unless create_purchase
通知
終了
プライベート
attr_reader :buyer, :cart
def check_stock
Services::CheckStock.call(cart: cart)
終了
def create_purchase
Services::CreatePurchase.call(buyer: バイヤー, cart: カート).call
終了
def notify
Services::NotifyBuyer.call(buyer: バイヤー)
end
終了
終了
終了
この新しい変更により、ステップがどのように定義されているかが明確になり、各ステップの結果をSuccess()とFailure()で管理できるようになった。
コントローラで新しいユースケースを呼び出し、最終結果に応じてレスポンスを準備する準備ができました。
クラス コントローラ
def購入
UseCases::Buyer::Purchase.new.call(
buyer: purchase_params[:buyer]、
cart: purchase_params[:cart].
) do |result|
result.success do
...
終了
result.failure do
...
終了
終了
...
終わり
...
終わり
この例は検証によってさらに改善できるが、このパターンの威力を示すにはこれで十分だ。
正直に言おう。 ユースケースパターン はとてもシンプルで、サービス・オブジェクトによく似ていますが、この抽象化レベルはアプリケーションに大きな変化をもたらします。
新しい開発者がプロジェクトに参加し、use_casesフォルダを開くことを想像してみてください。第一印象として、彼はシステムで利用可能なすべての機能のリストを持ち、1つのユースケースを開いた後、彼はロジックに深く入ることなく、その機能に必要なすべてのステップを見るでしょう。この秩序とコントロールの感覚は、このパターンの大きな利点です。
これを道具箱に入れておけば、もしかしたら将来、有効活用できるかもしれない。