コントローラーのパブリックインターフェースを「どのように」から「何を」へリファクタリングする

# 何をしたか
コントローラーのアクション内の処理に関する **6行** の記述を、
処理内容がわかりやすいメソッド名にし、 **1行** にしました。
また、**「どのように」から「何を」** を意識するようにリファクタリングしました。

# どんなメリットがあるか
- createアクションの可読性が上がる

# 具体的な内容
`ApplyController#create`での処理です。
(Applyが単数系なのは、このコントローラがAPIからのリクエストを受け取るコントローラであり、
indexなどの複数のリソースを扱わないためです。)

Applyという応募情報を管理するテーブルがあり、createアクションは応募情報を作成する処理です。
Applyには下記のカラムが存在します。（便宜上、必要最低限のカラムのみとしています。）

- メールアドレス...`email`
- 生年月日...`birthday`
- A社のアフィリエイトから訪問した際にパラメータに含まれるID...`a_id`
- B社のアフィリエイトから訪問した際にパラメータに含まれるID...`b_id`

作成処理(createアクション)では、
アフィリエイト経由の場合、
経由もとアフィリエイトへ、成果HTTPリクエストを送りたいという仕様でした。

また、アフィリエイトは2社利用されており、それぞれの送信の条件が異なりました。

a_idはA社へ、
b_idはB社へ送信します。

送信条件としては、
どちらも開発環境では送信させたくないため、環境での条件が科せられています。

A社に関しては、すでにUserという会員情報を管理するテーブルに、
同じメールアドレスでレコードが存在する場合は送信させたくないようです。

また、A社は20~50代の応募のみを対象としています。

```ruby
class ApplyController < ApplicationController

  def create
    # 受け取ったパラメータでインスタンス化
    apply = Apply.new(apply_params)
    # Userという会員情報を管理するテーブルにすでに存在するメールアドレスの応募か確認している
    is_exists_user_email = User.exists?(email: apply.email)

    # 処理が続く...

    if apply.a_id.present? && !is_exists_user_email && apply.include_age?(range: 20..59)
        && (Rails.env.production? || Rails.env.pre?)
      Net::HTTP.get_print(URI.parse("https://a-company?a_id=#{apply.a_id}"))
    end

    if apply.b_id.present? && (Rails.env.production? || Rails.env.pre?)
      Net::HTTP.get_print(URI.parse("https://b-company?b_id=#{apply.b_id}"))
    end

    # 処理が続く...
  end

end
```

A社へ送信するリクエストの条件に利用している`Apply#include_age?`は
Applyのbirthdayカラムから年齢を算出し、`Apply#include_age?`の`range`キーワード引数に渡された範囲の年齢かを真偽値で返すメソッドです。

```ruby
class Apply
  # 生年月日から年齢を返すメソッド
  def age_calculation; end

  # 年齢がRangeオブジェクトの範囲内かを真偽値で返すメソッド
  def include_age?(range:)
    range.include?(age_calculation)
  end
end
```

# これらを踏まえてやりたかったこと
- createアクションの可読性を上げる
- 「どのように」から「何を」を意識した設計にする

# どのようにしたか
createアクション内では
- どんな条件で
- どこへ
- 何をするか

が全て指示されているような設計となっていました。
これは[参考文献](https://www.amazon.co.jp/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E8%A8%AD%E8%A8%88%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%EF%BD%9ERuby%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8B-%E9%80%B2%E5%8C%96%E3%81%97%E3%81%A4%E3%81%A5%E3%81%91%E3%82%8B%E6%9F%94%E8%BB%9F%E3%81%AA%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E8%82%B2%E3%81%A6%E6%96%B9-Sandi-Metz-ebook/dp/B01L8SEVYI/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=15HTQ7LABH4C9&keywords=%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E8%A8%AD%E8%A8%88&qid=1680305763&s=instant-video&sprefix=%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E8%A8%AD%E8%A8%88%2Cinstant-video%2C233&sr=1-1)の4章でも取り上げられていた

:::note info
犬を散歩へ連れ出すことを考える。
犬の**足**に直接「歩け」と命じるのはおかしい。
この場合、**犬に対して**命令し、自分の足の面倒は自分で見させるのが正しい方法。
:::
という考え方を適用してリファクタリングすることにしました。

具体的には **「アフィリエイトへ報酬リクエストを送信する」** という
`send_affiliate`プライベートメソッドとしました。

また`send_affiliate`では、A社とB社で条件が重複している箇所は一箇所にまとめ、
**早期リターン**を活用することで、コードを全て読まなくても良いようになりました。

```ruby
class HogeController < ApplicationController

  def create
    apply = Apply.new(apply_params)
    is_exists_user_email = User.exists?(email: apply.email)

    # 処理が続く...

    send_affiliate(apply, is_exists_user_email)

    # 処理が続く...
  end

  private

  def send_affiliate(apply, is_exists_user_email)
    return unless Rails.env.production? || Rails.env.pre?

    if apply.xuid.present? && !is_exists_user_email && apply.include_age?(range: 20..59)
      Net::HTTP.get_print(URI.parse("https://a-company?a_id=#{apply.a_id}"))
    end

    return if apply.cid.blank?

    Net::HTTP.get_print(URI.parse("https://b-company?b_id=#{apply.b_id}"))
  end
end
```

これにより、createアクション内のコードリーディング時には、
`send_affiliate`という単語から分かるように
「アフィリエイトへリクエストを送信しているんだな」ということがわかりやすくなりました。

# 今後
今回はパブリックインターフェースを単純にプライベートへ移しただけなのですが、
「そもそもHTTPリクエストなどを送るメソッドをコントローラーのプライベートメソッドに記述するのが最適なのか？」
といった疑問があがりました。

「モデルのメソッドとして管理する方法はどうなのか？」
「アフィリエイト専用のクラスを用意するのがいいのか？」
といった、方法も考えつきました。

これらをどのように判断するべきか、判断力がまだ備わっていないことを痛感したため、
参考文献を繰り返し読み込み、判断力を養っていこうと思います。

また、何かベストプラクティスがある場合ご教授いただけると幸いです。

# 参考文献

- [オブジェクト指向設計実践ガイド　～Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方](https://www.amazon.co.jp/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E8%A8%AD%E8%A8%88%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%EF%BD%9ERuby%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8B-%E9%80%B2%E5%8C%96%E3%81%97%E3%81%A4%E3%81%A5%E3%81%91%E3%82%8B%E6%9F%94%E8%BB%9F%E3%81%AA%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E8%82%B2%E3%81%A6%E6%96%B9-Sandi-Metz-ebook/dp/B01L8SEVYI/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=15HTQ7LABH4C9&keywords=%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E8%A8%AD%E8%A8%88&qid=1680305763&s=instant-video&sprefix=%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E8%A8%AD%E8%A8%88%2Cinstant-video%2C233&sr=1-1)