初めての個人開発 ①要件定義・設計編

# はじめに
私は2023年10月より、内定直結型エンジニア学習プログラム「アプレンティス」に2期生として参加しています。

https://apprentice.jp/lp

アプレンティスの課題としてオリジナルプロダクトを開発したので、その内容をまとめようと思います。
今回はその「要件定義・設計編」となります。

続きの「実装編」はこちらから ↓

https://qiita.com/mosyaneko/items/f87fa7f27467ef3e4cf3

# 目次
[1.テーマ(エレベーターピッチ)](#1-テーマ(エレベーターピッチ))
[2.課題定義](#2-課題定義)
[3.要件定義](#3-要件定義)
[4.業務フロー](#4-業務フロー)
[5.画面遷移図](#5-画面遷移図)
[6.ワイヤーフレーム](#6-ワイヤーフレーム)
[7.デザインカンプ](#7-デザインカンプ)
[8.プロトタイプ](#8-プロトタイプ)
[9.テーブル定義](#9-テーブル定義)
[10.ER図](#10-ER図)
[11.使用技術](#11-使用技術)
[12.システム構成図](#12-システム構成図)
[13.学んだこと](#13-学んだこと)

# 1. テーマ(エレベーターピッチ)
自身のベジタリアンの種類に対応したレシピやレストランを見つけたいあらゆる種類のベジタリアン向けの**VegEvery** (べジェヴリー)という"食"の情報サイトです。

ベジタリアンレシピやフード、レストランの情報を投稿・閲覧することができます。

ヴィーガンレシピサイト V-cookとは違って、ヴィーガン以外のベジタリアンの方も自分に合った食の情報を得ることができます。

# 2. 課題定義
## 誰のどんな課題を解決するのか？

ベジタリアンの方が、自身のベジタリアンの種類に対応した料理のレシピや、レストランのメニュー、お店の商品を探そうとする際、多種多様なベジタリアンの中の一つであるヴィーガンに対応しているか否かといった情報が多少あるだけで、その他のベジタリアン向けの情報がないといった、日本のベジタリアン界の課題を解決します。

## なぜそれを解決したいのか？

### 日本のベジタリアン人口の増加

[日本のベジタリアン・ヴィーガン・フレキシタリアン人口調査 by Vegewel](https://vegewel.com/ja/style/statistics3) によると、ベジタリアンもしくはヴィーガンは全体の 5.9％、週に 1 回以上、意識的に動物性食品を減らす食生活を送るフレキシタリアンは全体の 19.9％であり、いずれも 2 年前の調査から増加しています。近年、大豆ミートなどの代替肉や豆乳チーズ食品もスーパーに並ぶようになったことからも、ベジタリアンという食文化が広まっていることが伺えます。

<details><summary>もっと見る</summary>

私はインドのヴェーダ哲学に基づくヨーガの修行者です。その関係で、4 年程前にオリエンタル・ベジタリアンになりました。

オリエンタル・ベジタリアンとは、日本の精進料理がそれに該当し、肉・魚・卵の他に五葷（ごくん）と呼ばれる野菜(玉ねぎ、ねぎ、にんにく、らっきょう、にら)を食べないベジタリアンを指します。一切の動物性食品を避けるヴィーガンとは違い、殺生をしない動物性食品(乳製品やはちみつ)はありがたくいただきます。 その他にも、肉は食べないが魚は食べるペスコタリアン、卵は食べるオボ・ベジタリアン、完全に肉を食べないまで行かないものの減らそうと努めている、いわゆる「ゆるベジタリアン」のフレキシタリアンなど、一言に「ベジタリアン」と言っても、実はたくさんの種類のベジタリアンが存在しています。

※ヴィーガンとフレキシタリアンをベジタリアンと別の分類とする考え方もありますが、ここでは大きな枠でのベジタリアンに含まれることとします。
</details>

### ベジタリアン向けの食の情報は、ヴィーガンに偏っている


日本において、ベジタリアンが食べられるものを探そうとした時に参考にできるのは、唯一ヴィーガンの方向けの情報です。
ヴィーガン専門レシピサイトであったり、レストランのメニューや商品についているヴィーガンマークといったヴィーガン向けの情報であれば、現在の日本において多少目に入るようになってはきています。

しかし、オリエンタル・ベジタリアンとなるとそうはいきません。野菜の中でも、先述した五葷を食べないので、ヴィーガンメニューにも食べられないものがあるのです。
ヴィーガンメニューは肉などで味を出せないので、特にたまねぎやにんにくが多く使われる傾向があるため、ヴィーガンメニューでも意外と食べられないものが多いのです。

<details><summary>もっと見る</summary>
例えば、ペスコタリアンやオボ・ベジタリアンの方であれば、ヴィーガンメニューはすべて食べることができます。
ヴィーガンの方が食べられるものに加えて魚も食べられるのがペスコタリアン、ヴィーガンの方が食べられるものに加えて卵も食べられるのがオボ・ベジタリアンだからです。

そのような、ヴィーガン以外の「ヴィーガンメニューが食べられるベジタリアン」においても、本来ヴィーガンメニュー以外にも食べられるものがたくさんあるはずなのに、情報がヴィーガンメニューに限られてしまいます。
</details>

### ベジタリアン以外の方との関わりの中で

ベジタリアンの食の情報が少ないということは、自分だけの問題に留まりません。
家族や友人などと外食に行く際には、少ない情報の中から一緒に行けるレストランを探すか、自分だけ何も食べないといった選択をすることになってしまいます。
もっとレストランの情報たくさんあれば、そういった際の選択肢が広がります。

また、あらゆるレシピサイトから自身が食べられるレシピを探し集め、管理するのはとても大変です。
対応するレシピを簡単に検索、保存・整理することができたら、自分自身はもちろん、ベジタリアンであることに配慮して料理を作ってくれる家族等への負担も大きく減らすことができるでしょう。

私自身も実際にこれらの問題に直面しています。そのため、地域社会の中であらゆる種類のベジタリアンがより良い食生活を送ることができるように、ヴィーガンだけでない様々なベジタリアンの種類に対応した情報が得られるサービスを作りたいと思ったのです。

## どうやって解決するのか？

ベジタリアンの種類に応じたレシピ情報、レストラン情報、ベジタリアン向け商品の情報が得られる Web サイトを作成する。

# 3. 要件定義
## 機能要件

優先度
- P0：必須
- P1：ここまで実装することを想定
- P2：可能であれば実装したい

### ログイン機能
- 会員登録ができる(P1)
- ユーザー名、パスワード、ベジタリアンの種類を登録する(P1)
- ユーザーアイコン（画像）が設定できるデフォルトでは、ベジタリアンの種類に応じたアイコンに設定される(P1)
- ログイン状態でのみ、以下の機能が利用できる
- 情報の新規投稿・編集・削除(P0)
- 投稿への「いいね」やコメント(P1)
- お気に入りの情報を保存する「本棚」の利用(P1)

### レシピ情報
- タグを付けることで、どのベジタリアンの種類に対応した情報なのかが分かる
- よく使われるタグはデフォルトで投稿画面に表示されていて、ワンクリックで付けられる(P0)
- デフォルト以外にも、自分で入力してタグ付けすることができる(P1)
- レシピでは、どのベジタリアンの種類に対応したレシピになっているか、登録された材料から判別して自動でタグ付けされる（確認画面あり）(P2)
- レシピの材料をクリックすると、ベジタリアン向け商品情報から該当商品を検索した一覧が表示される(P2)
- レシピには他ユーザーがコメントできるようになっており、質問や、作ってみた感想などを投稿できる(P1)
- 「いいね」機能がある(P2)
- レシピには画像のほか、動画も投稿できる（動画のみは NG）(画像：P0、動画：P2)
- レシピ検索は、人気順（いいね数）、新着順でソートできる(P2)
- 自分用の非公開レシピも登録できる(P2)

### レストラン情報
- レストランに対して口コミのようにユーザーが情報を投稿する(P1)
- レストラン情報は Google マップと紐づいている(P1)
- 自身のベジタリアンの種類、食べられるメニュー、コメント、評価等の情報を投稿できる(P1)
- レストランマップはベジタリアンの種類など検索することができる(P2)

### ベジタリアン向けフードアイテム情報
- 購入できる場所、オススメの使い方などを投稿できる(P0)
- 投稿に対して、他のユーザーがコメントすることができる(P1)
- レシピ情報と紐づけて、その商品を使ったレシピ一覧を表示できる(P2)

### 本棚機能
- 「マイページ」に情報整理のための「本棚」を設置できる(P2)
- 「本棚」にはお気に入りのレシピ、レストラン、商品の情報を保存できる(P2)
- 「本棚」は複数作ることができ、「お菓子」や「和食」など任意の名前をつけることで情報の整理ができる(P2)
- 自身で作成した非公開レシピやメモも登録できる(P2)

【その他】
- ヘルプページの作成(P2)

## 非機能要件
- スマホに対応(P0)
- PC に対応(P1)
- タブレットに対応(P2)
- 個人情報は収集しない(P0)
- バックエンドのサーバーとデータベースを冗長化し、アクセスの集中時や災害時のリスクを軽減する(P1)
- エラーを監視し、早期に対応できるようにする(P2)

# 4. 業務フロー

https://www.figma.com/file/l51XtXJgiBdSs4HR8aK3JR/%E6%A5%AD%E5%8B%99%E3%83%95%E3%83%AD%E3%83%BC%E3%83%81%E3%83%A3%E3%83%BC%E3%83%88?type=whiteboard&node-id=0%3A1&t=u9WURY1y6iRRRLjd-1

# 5. 画面遷移図

https://www.figma.com/file/LmijGVXW3ACflPDjwuYvQ3/%E7%94%BB%E9%9D%A2%E9%81%B7%E7%A7%BB%E5%9B%B3?type=whiteboard&node-id=1%3A1857&t=lTVE9Nx0xtQ4mZaq-1

# 6. ワイヤーフレーム

https://www.figma.com/file/Oyln7YuWtLjIql201aiDXp/%E3%83%AF%E3%82%A4%E3%83%A4%E3%83%BC%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0?type=design&node-id=0%3A1&mode=design&t=rUvqnXZwaEZtptXD-1

# 7. デザインカンプ

https://www.figma.com/file/lQv4Tg7lPTZ5LyoTCeOWJO/%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3?type=design&node-id=0%3A1&mode=design&t=mzaQHBohNJZfSliW-1

# 8. プロトタイプ

https://www.figma.com/proto/lQv4Tg7lPTZ5LyoTCeOWJO/%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3?page-id=0%3A1&type=design&node-id=1-3&viewport=105%2C290%2C0.3&t=g9HdVMKQ0bFyt4pu-1&scaling=scale-down&starting-point-node-id=1%3A3&show-proto-sidebar=1

# 9. テーブル定義
以下のテーブルを作成しました。
- users
- personal_access_tokens
- social_accounts
- articles_of_recipe
- recipe_steps
- materials
- articles_of_item
- items
- reports
- comments_to_recipe
- comments_to_item
- tags
- article_of_recipe_tag
- article_of_item_tag
- restaurants
- reviews
- menus
- bookshelves
- bookshelf_article_of_recipe
- bookshelf_article_of_item
- likes

<details><summary>詳細はこちら</summary>

### users テーブル

| カラム名                  | データ型     | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------------------- | ------------ | ---- | -------- | ------------ | -------------------- |
| id                        | BIGINT       | PK   | true     |              | true                 |
| name                      | VARCHAR(50)  |      | true     |              |                      |
| password                  | VARCHAR(100) |      | true     |              |                      |
| secret_question           | VARCHAR(100) |      | true     |              |                      |
| answer_to_secret_question | VARCHAR(100) |      | true     |              |                      |
| vegetarian_type           | VARCHAR(20)  |      | true     |              |                      |
| icon                      | VARCHAR(255) |      | false    |              |                      |
| created_at                | TIMESTAMP    |      | true     |              |                      |
| updated_at                | TIMESTAMP    |      | true     |              |                      |

### personal_access_tokens テーブル

| カラム名     | データ型     | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------ | ------------ | ---- | -------- | ------------ | -------------------- |
| id           | BIGINT       | PK   | true     |              | true                 |
| tokenable    | VARCHAR(225) |      | true     |              |                      |
| name         | VARCHAR(225) |      | true     |              |                      |
| token        | VARCHAR(225) |      | true     |              |                      |
| abilities    | TEXT         |      | true     |              |                      |
| last_used_at | TIMESTAMP    |      | true     |              |                      |
| expires_at   | TIMESTAMP    |      | true     |              |                      |
| created_at   | TIMESTAMP    |      | true     |              |                      |
| updated_at   | TIMESTAMP    |      | true     |              |                      |

FOREIGN KEY (tokenable_id, tokenable_type) REFERENCES {任意のテーブル}(id)

### social_accounts テーブル

| カラム名    | データ型    | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ----------- | ----------- | ---- | -------- | ------------ | -------------------- |
| id          | BIGINT      | PK   | true     |              | true                 |
| user_id     | BIGINT      | FK   | true     |              |                      |
| provider_id | BIGINT      |      | true     |              |                      |
| provider    | VARCHAR(20) |      | true     |              |                      |
| created_at  | TIMESTAMP   |      | true     |              |                      |
| updated_at  | TIMESTAMP   |      | true     |              |                      |

FOREIGN KEY (user_id) REFERENCES users(id)

### articles_of_recipe テーブル

| カラム名            | データ型     | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------------- | ------------ | ---- | -------- | ------------ | -------------------- |
| id                  | BIGINT       | PK   | true     |              | true                 |
| user_id             | BIGINT       | FK   | true     |              |                      |
| title               | VARCHAR(50)  |      | true     |              |                      |
| thumbnail_path      | VARCHAR(50)  |      | false    |              |                      |
| thumbnail_url       | VARCHAR(255) |      | false    |              |                      |
| cooking_time        | VARCHAR(10)  |      | false    |              |                      |
| number_of_likes     | INTEGER      |      | true     | 0            |                      |
| servings            | VARCHAR(10)  |      | true     |              |                      |
| vegan               | BOOLEAN      |      | true     | false        |                      |
| oriental_vegetarian | BOOLEAN      |      | true     | false        |                      |
| ovo_vegetarian      | BOOLEAN      |      | true     | false        |                      |
| pescatarian         | BOOLEAN      |      | true     | false        |                      |
| lacto_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| pollo_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| fruitarian          | BOOLEAN      |      | true     | false        |                      |
| other_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| created_at          | TIMESTAMP    |      | true     |              |                      |
| updated_at          | TIMESTAMP    |      | true     |              |                      |

FOREIGN KEY (user_id) REFERENCES users(id)

FOREIGN KEY (material_id) REFERENCES materials(id)

### recipe_steps テーブル

| カラム名             | データ型     | キー | NOT NULL | デフォルト値 | オートインクリメント |
| -------------------- | ------------ | ---- | -------- | ------------ | -------------------- |
| id                   | BIGINT       | PK   | true     |              | true                 |
| article_of_recipe_id | BIGINT       | FK   | true     |              |                      |
| order                | INTEGER      |      | true     |              |                      |
| text                 | VARCHAR(255) |      | false    |              |                      |
| image_path           | VARCHAR(255) |      | false    |              |                      |
| image_url            | VARCHAR(255) |      | false    |              |                      |
| created_at           | TIMESTAMP    |      | true     |              |                      |
| updated_at           | TIMESTAMP    |      | true     |              |                      |

FOREIGN KEY (article_of_recipe_id) REFERENCES article_of_recipe(id)

### materials テーブル

| カラム名             | データ型    | キー | NOT NULL | デフォルト値 | オートインクリメント |
| -------------------- | ----------- | ---- | -------- | ------------ | -------------------- |
| id                   | BIGINT      | PK   | true     |              | true                 |
| article_of_recipe_id | BIGINT      | FK   | true     |              |                      |
| name                 | VARCHAR(50) |      | true     |              |                      |
| quantity             | VARCHAR(20) |      | true     |              |                      |
| unit                 | VARCHAR(20) |      | false    |              |                      |
| created_at           | TIMESTAMP   |      | true     |              |                      |
| updated_at           | TIMESTAMP   |      | true     |              |                      |

FOREIGN KEY (article_of_recipe_id) REFERENCES article_of_recipe(id)

### articles_of_item テーブル

| カラム名            | データ型     | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------------- | ------------ | ---- | -------- | ------------ | -------------------- |
| id                  | BIGINT       | PK   | true     |              | true                 |
| user_id             | BIGINT       | FK   | false    |              |                      |
| title               | VARCHAR(50)  |      | true     |              |                      |
| thumbnail_path      | VARCHAR(50)  |      | false    |              |                      |
| thumbnail_url       | VARCHAR(255) |      | false    |              |                      |
| number_of_likes     | INTEGER      |      | true     | 0            |                      |
| vegan               | BOOLEAN      |      | true     | false        |                      |
| oriental_vegetarian | BOOLEAN      |      | true     | false        |                      |
| ovo_vegetarian      | BOOLEAN      |      | true     | false        |                      |
| pescatarian         | BOOLEAN      |      | true     | false        |                      |
| lacto_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| pollo_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| fruitarian          | BOOLEAN      |      | true     | false        |                      |
| other_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| created_at          | TIMESTAMP    |      | true     |              |                      |
| updated_at          | TIMESTAMP    |      | true     |              |                      |

FOREIGN KEY (user_id) REFERENCES users(id)

### items テーブル

| カラム名           | データ型    | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------------ | ----------- | ---- | -------- | ------------ | -------------------- |
| id                 | BIGINT      | PK   | true     |              | true                 |
| article_of_item_id | BIGINT      | FK   | true     |              |                      |
| name               | VARCHAR(50) |      | true     |              |                      |
| where_to_buy       | VARCHAR(50) |      | false    |              |                      |
| price              | INTEGER     |      |          |              |                      |
| created_at         | TIMESTAMP   |      | true     |              |                      |
| updated_at         | TIMESTAMP   |      | true     |              |                      |

FOREIGN KEY (article_of_item_id) REFERENCES articles_of_item(id)

### reports テーブル

| カラム名           | データ型     | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------------ | ------------ | ---- | -------- | ------------ | -------------------- |
| id                 | BIGINT       | PK   | true     |              | true                 |
| article_of_item_id | BIGINT       | FK   | true     |              |                      |
| order              | INTEGER      |      | true     |              |                      |
| image_path         | VARCHAR(255) |      | false    |              |                      |
| image_url          | VARCHAR(255) |      | false    |              |                      |
| text               | VARCHAR(225) |      |          |              |                      |
| created_at         | TIMESTAMP    |      | true     |              |                      |
| updated_at         | TIMESTAMP    |      | true     |              |                      |

FOREIGN KEY (article_of_item_id) REFERENCES articles_of_item(id)

### comments_to_recipe テーブル

| カラム名             | データ型     | キー | NOT NULL | デフォルト値 | オートインクリメント |
| -------------------- | ------------ | ---- | -------- | ------------ | -------------------- |
| id                   | BIGINT       | PK   | true     |              | true                 |
| article_of_recipe_id | BIGINT       | FK   | true     |              |                      |
| user_id              | BIGINT       | FK   | false    |              |                      |
| number_of_likes      | INTEGER      |      | true     |              |                      |
| text                 | VARCHAR(255) |      |          |              |                      |
| created_at           | TIMESTAMP    |      | true     |              |                      |
| updated_at           | TIMESTAMP    |      | true     |              |                      |

FOREIGN KEY (article_of_recipe_id) REFERENCES article_of_recipe(id)

FOREIGN KEY (user_id) REFERENCES users(id)

### comments_to_item テーブル

| カラム名           | データ型     | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------------ | ------------ | ---- | -------- | ------------ | -------------------- |
| id                 | BIGINT       | PK   | true     |              | true                 |
| article_of_item_id | BIGINT       | FK   | true     |              |                      |
| user_id            | BIGINT       | FK   | false    |              |                      |
| number_of_likes    | INTEGER      |      | true     |              |                      |
| text               | VARCHAR(255) |      | true     |              |                      |
| created_at         | TIMESTAMP    |      | true     |              |                      |
| updated_at         | TIMESTAMP    |      | true     |              |                      |

FOREIGN KEY (articles_of_item_id) REFERENCES articles_of_item(id)

FOREIGN KEY (user_id) REFERENCES users(id)

### tags テーブル

| カラム名   | データ型    | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ---------- | ----------- | ---- | -------- | ------------ | -------------------- |
| id         | BIGINT      | PK   | true     |              | true                 |
| name       | VARCHAR(20) |      | true     |              |                      |
| created_at | TIMESTAMP   |      | true     |              |                      |
| updated_at | TIMESTAMP   |      | true     |              |                      |

### article_of_recipe_tag テーブル

| カラム名             | データ型  | キー | NOT NULL | デフォルト値 | オートインクリメント |
| -------------------- | --------- | ---- | -------- | ------------ | -------------------- |
| id                   | BIGINT    | PK   | true     |              | true                 |
| article_of_recipe_id | BIGINT    | FK   | true     |              |                      |
| tag_id               | BIGINT    | FK   | true     |              |                      |
| created_at           | TIMESTAMP |      | true     |              |                      |
| updated_at           | TIMESTAMP |      | true     |              |                      |

PRIMARY KEY (articles_of_recipe_id, tag_id),

FOREIGN KEY (article_of_recipe_id) REFERENCES article_of_recipe(id),

FOREIGN KEY (tag_id) REFERENCES tags(id)

### article_of_item_tag テーブル

| カラム名           | データ型  | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------------ | --------- | ---- | -------- | ------------ | -------------------- |
| id                 | BIGINT    | PK   | true     |              | true                 |
| article_of_item_id | BIGINT    | FK   | true     |              |                      |
| tag_id             | BIGINT    | FK   | true     |              |                      |
| created_at         | TIMESTAMP |      | true     |              |                      |
| updated_at         | TIMESTAMP |      | true     |              |                      |

PRIMARY KEY (article_of_item_id, tag_id),

FOREIGN KEY (articles_of_item_id) REFERENCES article_of_item(id),

FOREIGN KEY (tag_id) REFERENCES tags(id)

### restaurants テーブル

| カラム名            | データ型     | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------------- | ------------ | ---- | -------- | ------------ | -------------------- |
| id                  | BIGINT       | PK   | true     |              | true                 |
| name                | VARCHAR(100) |      | true     |              |                      |
| place_id            | VARCHAR(100) |      | true     |              |                      |
| latitude            | DOUBLE       |      | true     |              |                      |
| longitude           | DOUBLE       |      | true     |              |                      |
| star                | INTEGER      |      | true     |              |                      |
| vegan               | BOOLEAN      |      | true     | false        |                      |
| oriental_vegetarian | BOOLEAN      |      | true     | false        |                      |
| ovo_vegetarian      | BOOLEAN      |      | true     | false        |                      |
| pescatarian         | BOOLEAN      |      | true     | false        |                      |
| lacto_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| pollo_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| fruitarian          | BOOLEAN      |      | true     | false        |                      |
| other_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| created_at          | TIMESTAMP    |      | true     |              |                      |
| updated_at          | TIMESTAMP    |      | true     |              |                      |

### reviews テーブル

| カラム名        | データ型        | キー | NOT NULL | デフォルト値 | オートインクリメント |
| --------------- | --------------- | ---- | -------- | ------------ | -------------------- |
| id              | BIGINT          | PK   | true     |              | true                 |
| user_id         | BIGINT          | FK   | true     |              |                      |
| restaurant_id   | BIGINT 　　        | FK   | true     |              |                      |
| thumbnail_path  | VARCHAR(50)     |      | false    |              |                      |
| thumbnail_url   | VARCHAR(255)    |      | false    |              |                      |
| star            | INTEGER         |      | true     |              |                      |
| text            | VARCHAR(255)    |      | true     |              |                      |
| number_of_likes | INTEGER         |      | true     | 0            |                      |
| created_at      | TIMESTAMP       |      | true     |              |                      |
| updated_at      | TIMESTAMP       |      | true     |              |                      |

FOREIGN KEY (user_id) REFERENCES users(id)
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id)

### menus テーブル

| カラム名            | データ型     | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------------- | ------------ | ---- | -------- | ------------ | -------------------- |
| id                  | BIGINT       | PK   | true     |              | true                 |
| review_id           | BIGINT       | FK   | true     |              |                      |
| name                | VARCHAR(100) |      | true     |              |                      |
| price               | INTEGER      |      | true     |              |                      |
| vegan               | BOOLEAN      |      | true     | false        |                      |
| oriental_vegetarian | BOOLEAN      |      | true     | false        |                      |
| ovo_vegetarian      | BOOLEAN      |      | true     | false        |                      |
| pescatarian         | BOOLEAN      |      | true     | false        |                      |
| lacto_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| pollo_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| fruitarian          | BOOLEAN      |      | true     | false        |                      |
| other_vegetarian    | BOOLEAN      |      | true     | false        |                      |
| created_at          | TIMESTAMP    |      | true     |              |                      |
| updated_at          | TIMESTAMP    |      | true     |              |                      |

FOREIGN KEY (review_id) REFERENCES reviews(id)

### bookshelves テーブル

| カラム名   | データ型    | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ---------- | ----------- | ---- | -------- | ------------ | -------------------- |
| id         | BIGINT      | PK   | true     |              | true                 |
| user_id    | BIGINT      | FK   | true     |              |                      |
| name       | VARCHAR(50) |      | true     |              |                      |
| created_at | TIMESTAMP   |      | true     |              |                      |
| updated_at | TIMESTAMP   |      | true     |              |                      |

FOREIGN KEY (user_id) REFERENCES users(id)

### bookshelf_article_of_recipe テーブル

| カラム名             | データ型  | キー | NOT NULL | デフォルト値 | オートインクリメント |
| -------------------- | --------- | ---- | -------- | ------------ | -------------------- |
| bookshelf_id         | BIGINT    | FK   | true     |              |                      |
| article_of_recipe_id | BIGINT    | FK   | true     |              |                      |
| created_at           | TIMESTAMP |      | true     |              |                      |
| updated_at           | TIMESTAMP |      | true     |              |                      |

FOREIGN KEY (bookshelf_id) REFERENCES bookshelves(id)
FOREIGN KEY (article_of_recipe_id) REFERENCES articles_of_recipe(id)

### bookshelf_article_of_item テーブル

| カラム名           | データ型  | キー | NOT NULL | デフォルト値 | オートインクリメント |
| ------------------ | --------- | ---- | -------- | ------------ | -------------------- |
| bookshelf_id       | BIGINT    | FK   | true     |              |                      |
| article_of_item_id | BIGINT    | FK   | true     |              |                      |
| created_at         | TIMESTAMP |      | true     |              |                      |
| updated_at         | TIMESTAMP |      | true     |              |                      |

FOREIGN KEY (bookshelf_id) REFERENCES bookshelves(id)
FOREIGN KEY (article_of_item_id) REFERENCES articles_of_item(id)

</details>

# 10. ER図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3070527/7af5dda4-7ff4-4d93-a25e-92e69250d7c2.png)


# 11. 使用技術
## PC
- MacBook Air : M2 チップ / 16 GB

## プログラミング言語
- PHP 8.3.4
- JavaScript
- SQL

## フレームワーク
- Laravel Framework 10.46.0 (PHP)
- Next.js 14.1.4 (React 18.2.0)

## ライブラリ
### データフェッチ関連
- Axios 1.6.8
- SWR ^2.2.5
### UI 全般
- Tailwind CSS 3.3
- CSS Modules
- Shadcn/ui
- React-icons 5.0.1
### フォーム関連
- React Hook From 7.51
- Zod ^3.22.4
### 認証関連
- Laravel/Sanctum ^3.3
- Laravel/Socialite ^5.12
- js-cookie 3.0.5
### Google Map
- vis.gl/react-google-maps 0.8
他..

## データベース
- Postgre SQL 15.6
拡張機能: PGroonga (日本語全文検索)
GUI: Table Plus

## インフラ
- Docker 26.0.0
- docker-compose 1.29.2
- Debian 12
- Apache 2.4.57
- Vercel (フロントエンド)
- AWS (バックエンド)

### AWS 使用サービス
- VPC (Virtual Private Cloud)
- Public subnet ×2
- Private subnet ×1
- EC2 (Elastic Compute Cloud)
AMI イメージ: Ubuntu Server 22.04 LTS(HVM),SSD Volume Type
- ALB (Application Load Balancer)
- Route 53
- ACM (AWS Certificate Manager)
- S3 (Simple Storage Service)
- CloudFront

## バージョン管理
- Git/GitHub

## CI/CD
- PHP Code Sniffer
- PHP MD
- PHP Stan
- ES Lint ^8
- Prettier ^3.2.5
- GitHub Actions

## 監視ツール
- Sentry

# 12. システム構成図
![VegEvery.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3070527/77562f2d-9943-6719-c3b7-5f771f95dd0d.png)

# 13. 学んだこと
## 機能の優先度
今回、レシピ・フードアイテム・レストラン情報のシェアと、大きく分けて ３ つの機能を考えました。ただ、制作期間が限られているので、その３つを更に要件定義のところで分解し、**優先度**をつけて管理しました。
優先度をつけていたことで、実装の際に**次に何に取り掛かるか**を迷うことなく決めることができ、また、スケジュールを確認しながら軌道修正をすることができました。
まずは小さく開発してリリースを目指し、それを改善、アップグレードしていくというスタンスで進められたのはよかったと思っています。

## レスポンシブを考慮したデザイン
コア機能がレシピやレストランマップであることから、主にモバイルで使用されることを想定し、モバイルファーストでデザインを考えました。
アプレンティスの「オリジナルプロダクト設計」期間中には、モバイルのデザインのみ作成できました。
そのため、PC のレスポンシブデザインについてはモバイルのデザインを元にして、考えながら実装していきました。
事前に PC 版のデザインまで完成していれば、モバイルを想定して実装した際に、「ここは PC 版で違ったコンポーネントに入れ替えよう」等とイメージしながら、コンポーネントを作成できたなと思いました。

## 技術選定の重要性
設計する際に、なぜその技術やツールを採用するのかを明確にすることが重要だと学びました。
今回、コードを書き始めてから、「やっぱりこっちを使った方がいいかも」と思うことがあり、データベースが二転三転してしまいました。

設計段階では今まで使ってきた MySQL で考えていたのですが、非常に軽量ということでアドバイスをいただき、 SQLite に変更しました。

その後、画像をストレージに保存した方がいいのでは？と考えて色々調べた結果、無料枠のある Supabase が適しているのではないかと考えました。
レシピ検索の機能が必要なのですが、Supabase は PostgreSQL の日本語全文検索拡張機能である PGroonga が使える唯一の Baas だという情報を見つけたことと、モダンなツールを使ってみたいと思ったからです。

しばらくそれで進めていたのですが、その後無料枠がいっぱいになり、有料化すると結構な金額がかかってしまいそうなことが分かったので、バックエンドを設置している AWS の EC2 インスタンス上の Docker コンテナとして PostgreSQL のデータベースを設置し直し、ストレージは AWS の S3 に変更しました。

この過程で、複数あるデータベースの特徴を理解し、適切に選択することが大切だと学びました。
また、実際に開発・運用していく際には、費用がどれだけかかるのかという視点も必要でした。無料枠がある場合には、アプリケーションがどれだけのデータ量を必要とするかを見積もり、容量が増えた場合にデータベースを移行することなども考慮しなければならず、その点がこれからの課題です。

# まとめ
実装前のこれらの事前準備期間は時間との戦いというところもあり、ある程度で切り上げて次に進むことにしていました。理由は、実務においては、**納期を守る**ということが非常に重要であると思ったからです。そのため、決められた期限の中でできる限りのことをやるように努めました。

デザインについてはアプレンティスのカリキュラムにはありませんでしたが、職業訓練で Web デザイン科を修了しているのもあり、それを活かして Figma で作成ができたのはよかったと思います。

データベースについては二転三転した部分もありましたが、その分学びも大きかったので、次に活かしたいと思います。

与えられたものを作るのではなく、課題を解決するものを作るためにテーマを決めて、要件定義から経験できたことで、よりエンジニアとして実際に働くことを意識できたように思います。

次の記事 ②実装編 で、実際に制作したアプリケーションについて紹介すると共に、学んだことをまとめようと思います。

続きはこちら！ ↓

https://qiita.com/mosyaneko/items/f87fa7f27467ef3e4cf3