🦔

意味のなさそうなDTO(Data Transfer Object)

に公開

Daily Blogging93日目

設計を気にし始めると次から次へと概念とかパターンが出てきて大変

このクラス意味あるのかな?

APIのレスポンス用にデータを成形しているだけのクラスがある

※ブログ用に加工済み

class ResponseProducs
  attr_reader :a_ids, :b_ids

  def initialize(a_ids: [], b_ids: [])
    @a_ids = a_ids
    @b_ids = b_ids
  end
end

呼び出しもとはこんな感じ

a_ids = fetch_a_ids
b_ids = fetch_b_ids

ResponseProducs.new(a_ids: a_ids, b_ids: b_ids)

いやこれいるかな????
なんか冗長的だしクラス一つ増えて逆にわかりづらい気がする
複数箇所で利用しているわけでもない

こういうクラスはどう扱うべきなのか
色々調べてる時にDTOとの出会いを果たす

これ多分DTO

DTO(Data Transfer Object)とは、

異なるシステムや層間でデータを転送するためのオブジェクトです。DTOは通常、ビジネスロジックを含まず、データを持ち運ぶだけの役割を果たします。

by ChatGPT

異なる層っていうのは、クリーンアーキテクチャとかで見るあの層のこと

ResponseProducsもDTOといえそう
ビジネスロジックもないし

DTOの例

DTOの例でいうと、Serializerが当てはまる。

class UserDTO
  attr_reader :name, :email

  # userオブジェクトをそのまま受け取る
  def initialize(user)
    @name = user.name
    @email = user.email
  end

  # 必要に応じて他のメソッドを追加
  def full_name
    "#{name} <#{email}>"
  end
end

こう使う

user = User.find(1)

# 最終的にこれを返す
user_dto = UserDTO.new(user)

これはレスポンスに含めたい値を削り取ってる
こういうのは、レスポンスに含めてはいけない値の排除ができるので意味ある

こうすればいいんじゃないか

ResponseProducsは受け取ったデータをインスタンス変数に格納し直しているだけなので冗長的
他のAPIでは利用していない
こういう場合は、とりあえずリファクタの方法が2通りありそう

さよならResponseProducs

一つ目はResponseProducsの削除
クラス化している意味ないから削除する。

呼び出しもとでHashとかStructした方が良さそう

a_ids = fetch_a_ids
b_ids = fetch_b_ids

{ a_ids: a_ids, b_ids: b_ids }

Factoryメソッド

もう一つは、データの取得とオブジェクトの成形を同じクラスで受け持つ

class ResponseProducs
  attr_reader :a_ids, :b_ids

  def initialize(a_ids: [], b_ids: [])
    @a_ids = a_ids
    @b_ids = b_ids
  end

  # Factoryメソッドでデータ取得も統合
  def self.build
    a_ids = ModelA.fetch_a_ids
    b_ids = ModelB.fetch_b_ids

    new(a_ids: a_ids, b_ids: b_ids)
  end
end

個人的にはデータの整形とデータ取得ロジックの呼び出しが1箇所にまとまっている方が好き

Discussion