spree-dependencies について
Spreeのコアクラスを依存性注入で置き換える方法を解説する開発スキルです。Spreeアプリや拡張の開発者向けに、フックとクラス置換の選び方や、ワークフロー、サービス、APIシリアライザーの契約を説明します。
主な機能
- 01クラス置換が適切な場面の判断
- 02ワークフローとサービスの置換設定
- 03依存キーを使ったAPIシリアライザーの拡張
- 04旧依存キーへの対処
- 05Spreeのアクセサー経由で上書きを適用
活用シーン
- 01カスタムのカートまたは注文ワークフローの実装
- 02StoreまたはAdmin APIシリアライザーへのフィールド追加
- 03旧サービスの上書きをワークフローへ移行
- 04拡張コードでホストアプリの上書きを反映
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ spree/ agent-skills --skill spree-dependenciesインストール後、Claude Code で/spree-dependenciesを入力して読み込まれたか確認します。
詳細
Spree の依存関係(クラス全体の差し替え)
コマンドでは Spree CLI(
spree …)を使用します。CLI のない従来の Rails アプリでは、アプリのルートディレクトリからbin/rails …またはbundle exec rake …を実行してください。パスは、Rails アプリがserver/にあることを前提としています。
Spree.dependencies を使うと、サービス、ワークフロー、ポリシー、シリアライザーなどのコアクラスを独自のクラスに置き換えられます。Spree のコードは Spree::Carts::AddItem.call(...) ではなく、Spree.cart_add_item_workflow.call(...) を呼び出すため、置き換えたクラスは Store API、Admin API、ダッシュボード、ジョブ、インポートなど、あらゆる場所で実行されます。
まず、そのクラスを本当に置き換える必要があるか確認してください
クラスを置き換えると、Spree のリリースごとに再同期が必要なコピーを自分で管理することになります。クラスを差し替える理由の多くは、アップグレード後も維持されるワークフックで対応できます。
| 目的 | 差し替えの代わりに使うもの |
|---|---|
| カートへの追加/チェックアウト/キャンセル/返品をブロックする | validate フック — Spree.hooks.register('carts.add_item.validate', 'MyStore::Rule') |
| 税またはプロモーションの計算にデータを追加する | コンテキストフック(carts.recalculate_totals.set_tax_line_context、carts.recalculate.set_promotion_context) |
| 変更とあわせて行をアトミックに書き込む | ライフサイクルフック(carts.add_item.after_item_added、carts.complete.after_finalize) |
| ERP と同期する/メールを送信する/インデックスを再構築する | イベントサブスクライバー(spree-events-webhooks) |
| チェックアウト要件を追加する | Spree::Checkout::Registry.add_requirement(spree-checkout) |
| API レスポンスにフィールドを追加する | シリアライザーをサブクラス化する(以下を参照)— 小規模で正当な差し替えです |
フックの一覧については、spree-workflows スキルを参照してください。クラスの差し替えは、何かの計算方法を本当に変更する場合(異なるマージ戦略、カスタムの購入ボックスのランキング、B2B アクセスポリシー、コミッションモデル)に限ってください。
拡張ポイント
Spree::Core::Dependencies::INJECTION_POINTS_WITH_DEFAULTS(spree_core の lib/spree/core/dependencies.rb 内)では、167 個のコア拡張ポイントを定義しています。そのうち 106 個は *_workflow キーです。Spree::Api::ApiDependencies::INJECTION_POINTS_WITH_DEFAULTS(spree_api の lib/spree/api/dependencies.rb 内)には、**272 個の API 拡張ポイント(シリアライザーのみ)**があります。内訳は Store が 91 個(product_serializer)、Admin が 139 個(admin_product_serializer)、Seller が 42 個(seller_*_serializer)です。(数値は 6.0 のソースに基づいています。お使いのバージョンでは spree rake spree:dependencies:list を実行してください。)
名前から契約がわかります。
| 接尾辞 | 基底クラス | オーバーライドするもの |
|---|---|---|
*_workflow | Spree::Workflow のサブクラス | #perform(**keywords) — #call ではありません |
*_service | prepend Spree::ServiceModule::Base を含むプレーンなクラス | #call(**keywords) |
*_class、*_finder、*_handler、その他 | さまざまです。デフォルトを確認してください | デフォルトの説明に従ってください |
よく差し替えられるもの:
| キー | デフォルト | 備考 |
|---|---|---|
cart_add_item_workflow | Spree::Carts::AddItem | perform(variant:, cart:, quantity:, metadata:, options:, price:) |
cart_upsert_items_workflow | Spree::Carts::UpsertItems | 数量の設定/削除/一括処理 |
cart_recalculate_workflow | Spree::Carts::Recalculate | 価格を全面的に再計算します(配送、プロモーション、合計) |
cart_recalculate_totals_workflow | Spree::Carts::RecalculateTotals | 合計計算の単一拡張ポイント(割引/手数料/税の行 → 合計) |
order_recalculate_totals_workflow | Spree::Orders::RecalculateTotals | 注文確定後に再集計する対となる処理 |
cart_merge_workflow | Spree::Carts::Merge | ゲストのカートを顧客のカートにマージします |
carts_complete_workflow | Spree::Carts::Complete | チェックアウトの完了 — carts.complete.* フックを優先してください |
order_complete_workflow / order_cancel_workflow | Spree::Orders::Complete / ::Cancel | |
order_update_statuses_service | Spree::Orders::UpdateStatuses | payment_status / fulfillment_status を更新する唯一の処理 |
fulfillment_create_workflow / fulfillment_update_workflow | Spree::Fulfillments::Create / ::Update | |
payment_process_workflow、payment_capture_workflow、payment_void_workflow、refund_create_workflow | Spree::Payments::*、Spree::Refunds::Create | |
return_create_workflow … claim_*_workflow | Spree::Returns::*、Exchanges::*、Claims::* | |
storefront_access_policy_class | Spree::Storefront::AccessPolicy | Store API のレコード表示制御(所有権 + ゲストトークン)。B2B の拡張ポイントです |
products_for_context_service | Spree::Products::ForContext | 購入者向けにカタログを絞り込みます |
product_buy_box_service | Spree::Products::SelectBuyBox | マーケットプレイスで、どのバリアントを先頭に表示するかを決定します |
purchase_amount_due_at_checkout_service | Spree::Purchases::AmountDueAtCheckout | 頭金/後払い条件 |
tax_resolve_exemptions_service | Spree::Tax::ResolveExemptions | 税プロバイダー向けの免税証明情報 |
reporting_adapter | Spree::Reporting::Adapters::Live | |
search_product_presenter | nil(検索 gem によって設定されます) |
ability_class(Spree::Ability)は、すべてのスタッフ向け CanCanCan 権限を構築します。対象は Admin API の JWT スタッフ、Seller API、インポート/エクスポートです。Spree::Ability をサブクラス化し、initialize(user, options = {})(options[:store]、options[:resource])を維持して、super を呼び出してからルールを追加してください。これは権限キーのゲートの背後で動作します。シークレット API キー(Spree::ApiKeyAbility)はこのクラスを参照しません。spree-auth-permissions を参照してください。
ワークフローの置き換え
提供されているワークフローをサブクラス化し、同じキーワード引数を受け取る perform をオーバーライドします。perform のシグネチャーが契約です(不一致があると Ruby は ArgumentError を発生させます)。引数なしの super を呼び出すと、親のフロー全体を実行して、その Result を返します。
# server/app/workflows/my_store/carts/add_item.rb
module MyStore
module Carts
class AddItem < Spree::Carts::AddItem
# REQUIRED: without this the subclass dispatches 'my_store.carts.add_item.*'
# and every handler registered on 'carts.add_item.*' silently stops firing.
workflow_key 'carts.add_item'
def perform(variant:, cart: nil, **rest)
result = super # full Spree flow; a failure raises through, so code below runs only on success
external_step :notify_recommendations # network I/O — outside any transaction
result # Result#value is the line item
end
private
def notify_recommendations
MyStore::Recommendations.item_added(cart_id: cart.prefixed_id, sku: line_item.sku)
end
end
end
end
# server/config/initializers/spree.rb
Spree.cart_add_item_workflow = MyStore::Carts::AddItem
ワークフローを置き換える際のルール:
callではなく、performをオーバーライドします。Spree::Workflow#callは計測、failure/halt!の処理、補償処理を担います。- 親のキーを指す
workflow_keyを維持します(上記を参照)。これが、気付かれにくい不具合の最も一般的な原因です。 - 一部だけを変更する場合は、プライベートなステップメソッド(例:
add_to_line_item)をオーバーライドしてsuperを呼び出します。ただし、ステップ名は内部実装であり、リリースによって変更される可能性があります。フックのほうが安全です。 Spree::ServiceModule::Result(superの戻り値、またはsuccess(value))を返します。Result以外の戻り値はsuccessでラップされます。Spree.cart_add_item_workflowの呼び出し側は、親のResult#valueの型(ここではラインアイテム)を想定しています。変更しないでください。- ワークフローはほかの接続点(
step :recalculate, with: -> { Spree.cart_recalculate_workflow }など)を通じて連携するため、接続点を置き換えると、それに委譲するすべてのフローに影響します。
通常のサービスの置き換え
同じパターンで、call をオーバーライドします。
module MyStore
class CartUpdate < Spree::Carts::Update
def call(cart:, params:)
result = super
MyStore::ErpSync.enqueue(result.value.prefixed_id) if result.success?
result
end
end
end
Spree.carts_update_service = MyStore::CartUpdate
デフォルトのクラスを継承すると、Spree::ServiceModule::Base の配線(クラスレベルの .call、success/failure)を維持できます。必ず Result を返してください。
レガシーな *_service キーは保存されますが、適用されません
ワークフローになったシームでは、アプリを起動できるように以前の名前も引き続き受け付けますが、割り当てても何も起きません。値は保存され、非推奨の警告で「Spree ではもはや参照されません — オーバーライドは適用されません」と表示されますが、Spree は引き続きワークフローを呼び出します。以前のサービスコントラクト(call(order:, …) と run ステップ)に合わせて書かれたクラスは、ワークフローコントラクトと互換性がありません。ワークフローに移植してください。ワークフローをサブクラス化し、ロジックを perform/フックに移して、新しいキーを割り当てます。これらの名前は 6.1 で削除されます。
| レガシーキー(保存のみ) | 代わりに割り当てるキー | デフォルト |
|---|---|---|
cart_add_item_service | cart_add_item_workflow | Spree::Carts::AddItem |
cart_recalculate_service | cart_recalculate_workflow | Spree::Carts::Recalculate |
cart_merge_strategy | cart_merge_workflow | Spree::Carts::Merge |
carts_complete_service | carts_complete_workflow | Spree::Carts::Complete |
carts_upsert_items_service | cart_upsert_items_workflow | Spree::Carts::UpsertItems |
order_complete_service | order_complete_workflow | Spree::Orders::Complete |
order_cancel_service | order_cancel_workflow | Spree::Orders::Cancel |
fulfillment_create_service | fulfillment_create_workflow | Spree::Fulfillments::Create |
shipment_update_service, fulfillment_update_service | fulfillment_update_workflow | Spree::Fulfillments::Update |
payments_handle_webhook_service | payments_handle_webhook_workflow | Spree::Payments::HandleWebhook |
gift_card_apply/remove/redeem_service | gift_card_apply/remove/redeem_workflow | Spree::GiftCards::* |
単なる名前変更(適用され、警告が表示されます):checkout_add_store_credit_service → store_credit_apply_service、checkout_remove_store_credit_service → store_credit_remove_service。API では、stock_item_serializer → stock_level_serializer、digital_serializer → digital_asset_serializer(および admin_ の同名キー)。
削除済みのキー(割り当てると起動時に NoMethodError が発生します):carts_validate_service(完了時の検証は Spree::Checkout::Requirements と carts.complete.validate です)、checkout_next_service、checkout_update_service、checkout_complete_service、checkout_select_shipping_method_service、checkout_get_shipping_rates_service、cart_create_service、cart_update_service、cart_estimate_shipping_rates_service、cart_change_currency_service、line_item_*_service、shipment_*_service、account_*_service、およびレガシーな検索・ソート機能(products_finder、taxon_finder、variant_finder、…)。カートの作成・更新には carts_create_service/carts_update_service を使用します。チェックアウトの進行には checkout_advance_service を使用します。5.x から移行する場合は、spree-upgrade-5-to-6 をご覧ください。
API シリアライザー
API の依存関係はシリアライザーのみです。API ごとのサービスキーはありません。すべてのエンドポイントがコアの Spree.<key> シームを直接呼び出します。
# server/app/serializers/my_store/product_serializer.rb
module MyStore
class ProductSerializer < Spree::Api::V3::ProductSerializer # Alba resource
typelize brand_name: [:string, nullable: true]
attribute :brand_name do |product|
product.brand&.name
end
end
end
# config/initializers/spree.rb
Spree.api.product_serializer = 'MyStore::ProductSerializer' # Store API
Spree.api.admin_product_serializer = 'MyStore::Admin::ProductSerializer' # Admin API — separate key
- Store と Admin のキーは独立しています。
Spree::Api::V3::Admin::ProductSerializerは、定数を通じて 同梱のSpree::Api::V3::ProductSerializerを継承します。そのため、product_serializerを差し替えても Admin の出力は変わりません。両方にフィールドが必要な場合は、サブクラス化して両方を差し替えてください。 - ネストされた関連付けは、多くの場合アクセサーを通じて解決されます(
one :product, resource: proc { Spree.api.product_serializer })。その場合、こちらでも独自のクラスが使用されます。 @spree/sdkの型付きフィールドを追加するには、typelizeを使用してください(spree-typescript-sdkを参照)。
コントローラーごとのオーバーライド
1つのエンドポイントだけを変更するには、API コントローラーの名前付きフック(serializer_class、scope、collection_includes、model_class、resource_permitted_attributes)をデコレートします。
# server/app/controllers/spree/api/v3/store/products_controller_decorator.rb
module Spree::Api::V3::Store
module ProductsControllerDecorator
protected
def serializer_class
MyStore::DetailedProductSerializer
end
end
end
Spree::Api::V3::Store::ProductsController.prepend(Spree::Api::V3::Store::ProductsControllerDecorator)
コントローラーごとの *_service フックはありません。コントローラーはグローバルシームを呼び出します。spree-decorators をご覧ください。
独自のコードからのシームの利用
Spree.cart_add_item_workflow.call(cart: cart, variant: variant, quantity: 1) # resolves overrides
Spree.api.cart_serializer.new(cart, params: { store: current_store }).to_h
Spree::Carts::AddItem.call(...) # ❌ bypasses any override
特に拡張機能では、ホストアプリのオーバーライドと組み合わせられるよう、必ずアクセサーを経由してください。
構文:Spree.<key> = MyClass(推奨。定数名のタイプミスがあれば直ちにエラーになります)、複数の場合は Spree.dependencies { |d| d.<key> = MyClass }、または旧形式の Spree::Dependencies.<key> = 'MyClass'(文字列。遅延解決され、引き続きサポートされています)。
オーバーライドの確認
spree rake spree:dependencies:list # every seam with its current value, [OVERRIDDEN] marked
spree rake spree:dependencies:list | grep workflow
spree rake spree:dependencies:overrides # only overrides: default -> current (file:line that set it)
spree rake spree:dependencies:validate # constantize every seam; exits 1 on NameError — good CI check
継承したプロジェクトでは、まず overrides を実行してください。プログラムから同等の操作を行うには、Spree::Dependencies.current_values、.overridden?(:cart_add_item_workflow)、.override_info(:cart_add_item_workflow)、.validate! を使用します。API 側では Spree::Api::Dependencies を使用してください。旧形式の stash のみに対する書き込みはオーバーライドとして表示されません。アップグレード後、イニシャライザー内で _service = を検索してください。
注意点
- 読み込み順序: オーバーライドは
config/initializers/spree.rbに記述します。2つの gem が同じキーを設定した場合、最後の代入が優先されます。spree:dependencies:overridesで確認してください。 - ワークフローを差し替えたらフックが失われた →
workflow_keyがありません(上記を参照)。 - アップグレード後、オーバーライドが「何もしない」 → 旧形式の
*_serviceキーを代入しています。起動ログで「NO LONGER CONSULTED」の非推奨警告を確認してください。 - 動作の欠落:
superを呼び出さない置き換えでは、標準の予約、価格プロバイダー、利用可能性の確認、再計算、イベントを再実装する必要があります。まず親のperformを最後まで読んでください。 - 呼び出し時の
ArgumentError→performのシグネチャが、呼び出し側から渡されるキーワード引数を受け付けていません。**restを受け入れて転送してください。
さらに読むには
node_modules/@spree/docs/dist/developer/customization/dependencies.md(https://spreecommerce.org/docs/developer/customization/dependencies) — 注:公開ページの一部には、API v2 のstorefront_*キーがまだ記載されています。ソース内の定数を正式な情報として参照してください。node_modules/@spree/docs/dist/developer/customization/workflows.md— フックと、推奨される代替方法- ソース:
spree_coreのlib/spree/core/dependencies.rb(INJECTION_POINTS_WITH_DEFAULTS、LEGACY_WORKFLOW_KEYS、LEGACY_SERVICE_KEYS、RENAMED_SERVICE_KEYS)、spree_apiのlib/spree/api/dependencies.rb、lib/tasks/dependencies.rake - 関連スキル:
spree-workflows、spree-customization、spree-decorators、spree-extensions