想定する読者
- ポートフォリオとしてクイズ系アプリを作成している方
アンチパターン例 3つ
例えば4択クイズを作りたいというとき、以下のようなDB設計を思いつくかもしれません。
※筆者が思いつきました。
これらは明確なアンチパターンです。
何故アンチパターンかというと、仕様変更に弱いからです。
part1 マジックナンバーカラム
id | question | choice1 | choice2 | choice3 | choice4 | answer_number |
---|---|---|---|---|---|---|
1 | 次のうち偶数なのは? | 3 | 7 | 8 | 1 | 3 |
2 | 「令和」はどう読む? | れいわ | へいせい | しょうわ | たいせい | 1 |
仕様変更さん「○×クイズもできるようにしたい〜〜選択肢を2つに減らすだけだし簡単ですよね?選択肢も4つだけじゃなくて、5つとかもあり得るかも♪」
教訓: カラム名にマジックナンバーが入ってたらアンチパターンの合図
part2 フィールドに配列ぶちこみ
| id | question | choices | answer_number |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| 1 | 次のうち偶数なのは? | [3, 7, 8, 1] | 3 |
| 2 | 「令和」はどう読む? | [れいわ, へいわ, しょうわ, たいせい] | 1 |
| 3 | 今は平成か? | [○, ×] | 2 |
| 4 | 次のうち一番大きい数字はどれか? | [5, 10, 9, 8, 4, 1] | 2 |
仕様変更さん1「よーし、検索機能を入れることになりました!検索機能って基本的な機能なので簡単ですよね?選択肢の中の文でも検索がかけられるようにしたいです♪」
仕様変更さん2「特定の選択肢は後で編集ができるようにしたいですね〜〜ユーザーさんって誤字とかしちゃうこともあるじゃないですか♪」
教訓: フィールドに配列を入れたくなったらアンチパターンの合図
part3 テーブルを分けて1対多にする(が、分けきれてない)
questionsテーブル
id | content | answer_number |
---|---|---|
1 | 「令和」はどう読む? | 1 |
2 | 今は平成か? | 2 |
choicesテーブル
id | content | question_id |
---|---|---|
1 | れいわ | 1 |
2 | へいせい | 1 |
3 | しょうわ | 1 |
4 | たいせい | 1 |
5 | ○ | 2 |
6 | × | 2 |
仕様変更さん「複数の答えを用意できるようにしたいです! この複雑な世の中で、答えって一つじゃないと思うんです♪」
何がダメだったのか: answer_numberはどちらかというとchoicesテーブルにあるべき情報だと思います。関心の範囲と、テーブルの範囲を合致させるように気をつけると、仕様変更による影響が少なくできると思います。
(おそらく)正しい設計
questionsテーブル
id | content |
---|---|
1 | 「令和」はどう読む? |
2 | 次のうち偶数のものはどれか? |
choicesテーブル
id | content | is_answer | question_id |
---|---|---|---|
1 | れいわ | true | 1 |
2 | へいせい | false | 1 |
3 | しょうわ | false | 1 |
4 | たいせい | false | 1 |
5 | 11 | false | 2 |
6 | 12 | true | 2 |
7 | 15 | false | 2 |
8 | 99 | false | 2 |
9 | 18 | true | 2 |
アンチパターンと上から目線で言いながら、これが最適かと言われれば微妙です笑
ただし、現役のエンジニアさんに見せたら「良いんじゃない?」って言われたんで、そこまで悪くない設計だとは思います。
マサカリ頂けると有難いです。
ちなみにRailsだったら、どんなmigrationファイル作ればいいの
$ ruby -v
ruby 2.6.5
$ rails -v
Rails 5.2.4
class CreateQuestions < ActiveRecord::Migration[5.2]
def change
create_table :questions do |t|
t.text :content, null: false # 問題文の中身
t.references :user, foreign_key: true # クイズを作った人を想定
t.timestamps
end
end
end
class CreateChoices < ActiveRecord::Migration[5.2]
def change
create_table :choices do |t|
t.text :content, null: false
t.boolean :is_answer
t.references :question, foreign_key: true
t.timestamps
end
end
end