【Rails】furimaアプリ DB設計

先日実装した「furimaアプリ」の実装について
記録を残すため記事を書いてみようと思います。

FURIMAの必須機能

ユーザーのログイン機能

商品出品 / 購入

必要なテーブルを洗い出す

「ユーザー情報」「商品情報」「発送先情報」を保存する必要がある。
ここまではわかったのですが、「売り切れ」を表示するために、商品に「購入記録」を結びつける必要があると考えました。

「購入記録」と「発送先情報」のテーブルを一緒にすることもできますが、発送先情報だけでも郵便番号、都道府県、市区町村など多くの情報を含むので、管理しやすいように別々の情報として考えました。

よって、最初に必要なテーブルは「ユーザー情報」「商品情報」「購入記録」「発送先情報」の4つとなりました。

実装条件を見て必要なカラムを洗い出す

今回、ActiveHashを使用する指示があったものは「〜_id」というカラム名にしました。(ActiveHashについては別の記事で書きます)

保存している情報が分かるように命名するのは、私にとってまだまだ課題だな。と感じました。

テーブル カラム カラム名
ユーザー(users) ニックネーム nickname
メールアドレス email
パスワード encrypted_password
苗字(全角) family_name
名前(全角) first_name
苗字カナ(全角) family_name_kana
名前カナ(全角) first_name_kana
生年月日 birth_day
商品情報(items) 出品者 user
商品名 name
商品の説明 explanation
カテゴリー category_id
商品の状態 condition_id
配送料の負担 postage_id
発送元の地域 prefecture_id
発送までの日数 preparation_id
価格 price
発送先情報(addresses) 購入記録 order
郵便番号 post_code
都道府県 prefecture_id
市区町村 city
番地 address
建物名 building_name
電話番号 phone_number
購入記録(orders) 誰が user
どの商品を item

テーブル間の関係性を考える

①ユーザー情報と商品情報のテーブルについて

「新規登録(ログイン)をしたユーザーは、いくつ商品を出品することができるか?」という点に着目。
自分が普段利用している某フリマアプリを想像。たくさん出品してるな。
ということで、ユーザーはたくさんの商品を持つことができる。
自分が出品した商品の出品者は自分一人だけのはずなので、ユーザーと商品情報は1対多になりました。

②ユーザー情報と購入記録のテーブルについて

「新規登録(ログイン)をしたユーザーは、いくつ商品を購入することができるか?」という点に着目。
自分が某フリマアプリで何を買ったかな?ダンスの衣装、結婚式の小物...たくさん買ってる! お世話になってるなあ。
自分の名前は購入記録にたくさん刻まれている。
1つの商品に対して、買主は1人なので、ユーザーと購入記録は1対多になりました。

③商品情報と購入記録のテーブルについて

1つ1つの商品は、購入されると売り切れとなります。つまり、「1つの商品につき、購入できるのは1回」ということです。
1対1の場合、has_oneというアソシエーションの知識が必要になります。

「一方が存在しないと存在できない」という考え方が必要になってきます。
商品は購入記録がなくても存在することができますが、 購入記録は商品ありきの購入記録です。
なので、親子関係を考えると、商品が親、購入記録は子になります。
親が「has_one」のアソシエーションを持つので、商品情報にhas_oneのアソシエーションを持たせました。

④発送先情報と購入記録のテーブルについて

初め、ユーザーに発送先情報が紐づくのかなと思っていたのですが...
実装条件に「配送先の住所情報も購入の都度入力」とあり、必ずしもユーザーの住所が発送先になるわけではないと認識。
じゃあ発送先はどこに紐づくんだろう。と少し悩みました。

購入記録に発送先が記録されるので、ここだな。と認識。
1つの購入記録ごとに1つの発送先が登録されるので、ここも1対1。
初めは同じ住所あるかもしれん!って思い、1対多?とも思ったのですが、
実装条件に「配送先の住所情報も購入の都度入力」(強調)という仕様になっており、1つの商品は1つの住所にしか行けないと考えることができるので、1対1になる。

購入記録は「誰が」「何を」買ったのか記録されるテーブルで、発送先がなくても行き先待ちの状態で存在はできます。
発送先は購入記録がないと行き先として登録してもらえません。
なので、購入記録に親として「has_one」 のアソシエーションを持たせました。

ここが1番躓いたなと。実装条件をしっかり読み取るのって難しい。

READMEに記載

実装条件とアソシエーションを踏まえてOptionsを記載。
発送先の建物名は任意であるため、Optionsは不要。

今回、ActiveHashを使用する指示があったものは「integer型」にしてます。(ActiveHashについては別の記事で書きます)

README.md

# README

## users テーブル

| Column             | Type   | Options     |
| ------------------ | ------ | ----------- |
| nickname           | string | null: false |
| email              | string | null: false, unique: true |
| encrypted_password | string | null: false |
| family_name        | string | null: false |
| first_name         | string | null: false |
| family_name_kana   | string | null: false |
| first_name_kana    | string | null: false |
| birth_day           | date   | null: false |

### Association

- has_many :items
- has_many :orders


## items テーブル

| Column           | Type       | Options                        |
| -----------------| ---------- | ------------------------------ |
| user             | references | null: false, foreign_key: true |
| name             | string     | null: false                    |
| price            | integer    | null: false                    |
| explanation      | text       | null: false                    |
| category_id      | integer    | null: false                    |
| condition_id     | integer    | null: false                    |
| postage_id       | integer    | null: false                    |
| prefecture_id    | integer    | null: false                    |
| preparation_id   | integer    | null: false                    |

### Association

- belongs_to :user
- has_one :order

## addresses テーブル

| Column           | Type       | Options                        |
| -----------------| ---------- | ------------------------------ |
| order            | references | null: false, foreign_key: true |
| post_code        | string     | null: false                    |
| prefecture_id    | integer    | null: false                    |
| city             | string     | null: false                    |
| address          | string     | null: false                    |
| building_name    | string     |                                |
| phone_number     | string     | null: false                    |

### Association

- belongs_to :order

## orders テーブル

| Column           | Type       | Options                        |
| -----------------| ---------- | ------------------------------ |
| user             | references | null: false, foreign_key: true |
| item             | references | null: false, foreign_key: true |

### Association

- belongs_to :user
- belongs_to :item
- has_one :address

感想

DB設計をするためには

登場人物を書き出して、システムを使う人の目線で「このシステムは、こんなことができるのね」と、考えることが大切だと感じました。
考えるためには、ユースケース図を用いるのがいいのかなと。
オリジナルアプリを作るときにもこの経験を活かしていきたいです。