販売チャネルごとの条件分岐を解消 – クラスのリファクタリング

はじめに

こんにちは。デザインシステム室の森口です。

弊社で展開している健康食宅配サービス「ミールタイム」で提供している商品には、単体商品と、複数商品をまとめたセット商品が存在します。

また、販売チャネルもWeb・電話・定期便など複数に分かれており、商品が「販売可能かどうか」を判定する処理(salable?)には、それぞれ異なる条件が必要です。

当初は、こうした条件分岐をすべて一つのクラス(SalesItem)内で対応しており、以下のような課題がありました。

課題

販売チャネルごとのルール(例:Webでは販売不可、電話では可能など)をSalesItem内で分岐していたため、責務が肥大化。

条件分岐がネストし、可読性・保守性が低下。

改修方針

単体商品とセット商品の共通属性・処理を抽出し、共通インターフェースとしてSalesItemにまとめる。

販売チャネルごとの処理はサブクラス(例:SalesItem::WebChannel)へ分離。

条件分岐のロジックは必要最小限にとどめ、salable?の読みやすさを担保。

エラー種別ごとに専用の例外を定義し、意図しないエラーとの区別を明確化。

実装概要

以下は、現在のSalesItemの構成の一部です(簡略化)。

SalesItem.create(code: "XXXX", sales_channel: :web).salable?

def self.create(code: nil, sales_channel: :web)
  klass = channel_class(sales_channel)
  klass.new(code: code)
end

def self.channel_class(sales_channel)
  case sales_channel
  when :web then SalesItem::WebChannel
  ...
  else raise ArgumentError, "Invalid sales channel: #{sales_channel}"
  end
end

商品種別とチャネルを意識することなく、共通のインターフェースで販売可能判定が可能になっています。

内部的には、単体商品セット商品 かを判定し、それぞれに対応する属性を設定。さらに、チャネルに応じて SalesItem::WebChannelSalesItem::TelChannel などのサブクラスで判定ロジックを委譲します。

def salable?(check_params)

  case self.type
  when セット商品
    check_selection(check_params)
  when 単体商品
    single_item_check(...)
  else
    raise InvalidTypeError
  end

  true
end

チャネル固有の判定(例:おせち商品はWebチャネルでは販売不可)などは、規定クラスのメソッドを各サブクラスでオーバーライドして対応しています。
以下ではchannel_specific_checksメソッドをオーバーライドしていますが、基底クラスに定義されている元々のchannel_specific_checksメソッドは何も処理を行いません。

class SalesItem::WebChannel < SalesItem
  def channel_specific_checks(check_params)
    check_osechi_sales_channel(check_params)
    check_web_specific_restrictions
  end
end

テストの活用

今回の改修において、既存のRspecによるテストコードが大きく役立ちました。

SalesItemクラスを作成した当初から導入していたことで、salable?の挙動に対する期待値が明確になっており、内部のロジックを刷新してもテストが通るかどうかで正しさを担保できました。

結果として、リファクタリングにおける心理的ハードルを大きく下げることができました。

終わりに

業務ロジックの複雑さは避けられませんが、「どこで判断するか」「誰が責務を持つか」を丁寧に分けることで、見通しの良いコードが実現できます。

今回は販売チャネルごとの条件分岐を減らすために、SalesItemを中心として販売チャネルごとにサブクラスを持たせる設計を採用しました。要件追加にも柔軟に対応できる構成となったことで、開発・保守の負担を大幅に削減できていると思います。

現在デザイン・システム室では、新しいメンバーを募集しています。
皆様からのご応募をお待ちしております。