一問一答アプリの問題データを、どう設計したか
マナビポータルという、資格試験の一問一答アプリを作っています。このアプリでいちばん頭を悩ませたのが、「問題データの設計」でした。この記事では、最初の素朴な設計から、運用しながらどう育っていったかを、具体的なテーブルの形とともに書いてみます。
最初は「問題と答え」だけだった
作り始めたころは、シンプルに考えていました。問題文と、その答え。最低限これがあれば一問一答は成立する、と。最初のテーブルは、ほとんどこれだけでした。
create table questions (
id bigint generated always as identity primary key,
body text not null, -- 問題文
answer text not null -- 答え
);
ところが、実際に運用してみると、それだけでは足りないことがすぐに分かってきました。
運用して見えてきた「必要な情報」
使っていくうちに、こんな情報が欲しくなっていきました。
- 解説:答えだけでなく、なぜそうなるのかを示したい
- 難易度:やさしい問題と難しい問題を分けたい
- カテゴリ:分野ごとに出し分けたい
- 出典・改訂日:情報が古くなっていないか管理したい
結局、何度かデータの形を見直し、最終的にはこんな形に落ち着きました。
create table questions (
id bigint generated always as identity primary key,
category_id bigint references categories(id),
body text not null,
answer text not null,
explanation text, -- 解説
difficulty int default 2, -- 1=易 2=普通 3=難
source text, -- 出典
revised_at date, -- 改訂日
created_at timestamptz default now()
);
カテゴリは別テーブルに切り出して category_id で参照する形にしました。最初から文字列で category を持たせていると、表記ゆれ(「基本情報」「基本情報技術者」など)で泣くことになるからです。
「間違えた問題」をどう持つか
もう一つ悩んだのが、ユーザーの解答履歴です。「間違えた問題だけ復習したい」を実現するには、誰がどの問題をどう答えたかを持つ必要があります。ここは解答ごとに1行貯めるシンプルな履歴テーブルにして、「最後に間違えている問題」を後から絞り込めるようにしました。集計より先に、まず生の履歴を貯めておく——この発想は、データを扱うほかのプロジェクトでも共通して効いています。
最初から完璧を目指さない
この経験で学んだのは、最初から完璧なデータ設計を目指さないということです。個人開発では、作って、使って、必要なものが見えてから足す——そのくり返しのほうが、結果的にうまくいきます。もちろん後からのカラム追加は手間ですが、使ってもいない機能のために最初から複雑なスキーマを抱えるより、ずっと健全でした。
問題は今もAIに手伝ってもらいながら、少しずつ増やしています。アプリ全体の話は「資格学習アプリ「マナビポータル」を個人で作っている話」にまとめました。育てていく過程そのものが、個人開発の楽しさだなと感じています。