バイセル Tech Blog

バイセル Tech Blogは株式会社BuySellTechnologiesのエンジニア達が知見・発見を共有する技術ブログです。

form形式でJSONレスポンスを返す際の設計

はじめに

こんにちは、テクノロジー戦略本部の長兵衛です。

自分はこの記事で本アドベントカレンダーの4記事目となりますが、周りの方々の記事を読むと自分では思いつかなかった工夫がたくさんあって本当に面白いです。

是非、他にも興味のある記事を見ていただきたいと思います!

tech.buysell-technologies.com

では本題に行きます。

背景

現在開発中のシステムのバックエンドを担当しており、一部のAPIレスポンスを 「form形式で返す」というような実装をしました。この時に用いた設計が汎用的だなと思ったので記事にしようと思いました。

本質的な意味合いは異なりますが、見方によってはデザインパターンでいうアダプタっぽいかなと思っています。正しいかどうかは定かではないです。。

言語はruby、フレームワークはrailsを想定しています。

要件

testsテーブル

id
first
second

上記のようなテーブル(型は省略)があり、その1レコードを以下のようなJSONレスポンス形式で返したいです。

{
	"form_items" : [
		{
			"item_name" : "first",
			"value" : "hoge" # firstカラムの値
		},
		{
			"item_name" : "second",
			"value" : "fuga" # secondカラムの値
		}
	]
}

実際のシステムではitem_name, valueだけでなくメタデータなどが含まれて複雑な構造になっているのですが、今回は便宜上シンプルな設計にしています。今後カラム数が増えて1つ1つのカラムに対してform_itemsの配列の要素が比例すると想定してください。

設計と実装

5つのクラスを用意しました。

3つはFieldを定義するクラスです。抽象クラスとしてFieldBaseクラスを作成しています。

一見なぜこのような設計したか疑問に思うかもしれませんが、カラムとカラムに付随するメタデータが増えることを考えるとこのような設計が無難かなと思いました。

class FirstField < FieldBase
  COLUMN_NAME = 'first'
end

class SecondField < FieldBase
  COLUMN_NAME = 'second'
end

class Base
def initialize(record) @record = record end
def value @record.send(self.class::COLUMN_NAME) end
end

4つ目と5つ目はセットで説明します

まず、FormItemクラスではitem_nameとvalueのセットをインスタンスとして保持するために作成しています。具体的には5つ目のクラスを見ればわかると思います。

class FormItem
  attr_reader :item_name, :value

def initialize(field_instance)
@item_name = field_instance.class::COLUMN_NAME
@value = field_instance.value
end end

5つ目はform_itemの配列を作成するクラスです

class FormItemList
  attr_reader :form_items

  def initialize(record)
@form_items = []
# 以下form_itemの順番がレスポンスとして送るformの順番に直結する
form_item(FirstField.new(record))
form_item(SecondField.new(record))
end private def form_item(field_instance) @form_items += [FormItem.new(field_instance)] end end

あとは以下のようにcontrollerで呼び出してあげると、form形式にオブジェクト化されたものが要素となる配列が得られます

record = Test.first
form_items = FormItemList.new(record).form_items

pp form_items
# => [FirstFieldインスタンスから作成したFormItemインスタンス, FirstFieldインスタンスから作成したFormItemインスタンス]

ここまできたら配列内のオブジェクトをjsonへserializeするだけです。

serializeの方法は色々あるので説明は省きます。

まとめ

カラム数やメタデータが少ないとあまり恩恵を受けられていないように感じますが、増えた際にカスタマイズしやすいようになっているのがよかったかなと思います!

最後に、

バイセルはエンジニアを募集しております。

ご興味のある方は是非ご連絡ください!