システム開発でいちばん後から修正しにくい部分がある。データベースの設計だ。
テーブルの構造、カラム名、リレーションの持ち方——これを後から変えようとすると、関連するコードの修正が連鎖的に発生する。本番環境に大量のデータが入った後では、なおさら厄介だ。
だから普通の開発では、データベース設計に時間をかける。要件定義書を読み込み、ER図を引き、レビューし、また修正する。1〜2週間かけることも珍しくない。
私はこの部分を、AIとの対話で1日でやった。
最初に「言葉」で設計を固める
データベース設計をいきなりCREATE TABLE文から始めるのは悪手だ。先に「このシステムが扱う概念」を言葉で整理する必要がある。
私がAIに最初に投げた問いはこうだ。
「複数プレイヤーが参加するターン制の経営ゲームを作りたい。ゲームは複数同時に進行でき、各ゲームに複数のプレイヤーがいる。プレイヤーはターンごとに意思決定を入力し、結果が計算される。何のテーブルが必要か」
AIは即座に答えた。
必要なテーブルの候補:games(ゲームセッション)、players(参加者)、companies(各プレイヤーの会社状態)、turns(ターン管理)、decisions(意思決定の入力値)、results(ターン計算結果)、event_logs(イベント発生記録)。
概念は合っていた。ここから「なぜそう分けるのか」の対話が始まる。
「なぜ分けるのか」の対話
たとえばこんな問いを投げた。「companiesテーブルは現在の会社状態を持つのか、それとも毎ターンの状態をスナップショットで持つのか」
AIは両方のアプローチを説明した上で答えた。「ゲームのリアルタイム性と、過去ターンの振り返り機能の両方が必要なら、companiesは現在状態のみを持ち、resultsに毎ターンの結果スナップショットを保存する設計が適切です」
この説明は正しかった。そして私はすぐに次の問いを投げた。「ターン計算をロールバックする機能が必要だとしたら、この設計で問題ないか」
「ロールバック時にresultsレコードを削除し、companiesを前のターンの値に戻す処理が必要になります。audit_logsテーブルを追加して操作履歴を残しておくと、ロールバックの安全性が上がります」
こういう対話を、半日続けた。
実際に作ったスキーマ
最終的なテーブル構成はこうなった。games、players、companies、turns、decisions、results、event_logs、reflection_logs、diagnostics、audit_logs。
resultsテーブルには後から20個のカラムを追加することになった(製造原価・キャッシュフロー計算書)。ALTER TABLEで追加できる形にしておいたので、本番環境への適用も問題なく済んだ。
AIが気づかせてくれたこと
設計の途中で、AIが一つの指摘をした。「companiesテーブルにbankrupt_countカラムを追加することを推奨します。破産回数は企業の信用状態を表す重要な指標であり、診断レポートでの活用や、ゲームの教育効果の測定に使えます」
この指摘がなければ、bankrupt_countは後から追加することになっただろう。
AIとのデータベース設計は、「ベテランのDBエンジニアと相談しながら設計を固める」体験に近い。ただし24時間いつでも、どんな粒度の質問にも即答してくれる点が違う。
設計完了後、PythonのSQLAlchemy ORMモデルとDDLを生成するのに半日かかった。合計で丸1日。このスキーマは、その後半年以上、大きな変更なく本番稼働している。
スキーマの変遷と「失敗したDBデザイン」
1日で設計したと書いたが、正確には「1日で最初のバージョンを決めた」だ。その後、スキーマは少なくとも4回の主要な変更を経た。
最初に失敗したのは「companiesテーブルにDX投資の内訳を格納しようとした」設計だ。dx_l1、dx_l2、dx_l3、dx_l4という4つのカラムをcompaniesに直接持たせた。最初はこれで動いた。しかし「ターンごとのDX投資推移を振り返りたい」という要件が出たとき、この設計では対応できなかった。投資履歴がresultsテーブルにしか残らないため、「いつどのDXレベルに投資したか」が追跡できない。
2回目の失敗は「event_logsテーブルのデータ型」だ。イベントの影響値(demand_multiplierなど)をTEXT型で格納する設計にしてしまった。Pythonで計算するときにfloat()変換が必要になり、コードのあちこちに変換処理が散らばった。最終的にNUMERIC型に変更したが、既存データの移行に手間がかかった。
3回目は「decisionsテーブルのカラム設計」だ。最初はjsonb型で「全ての意思決定を1つのJSONで格納」した。開発の初期はこれが楽だった。しかしPythonのコードでJSONキーへのアクセスが増えるにつれ、「このキーは必ず存在するのか」という確認が煩わしくなった。最終的に個別カラムに変更した。変更後のコードは冗長になったが、型安全性と可読性が大幅に改善した。
「1日でスキーマを決めたこと」と「スキーマが完璧だったこと」は別の話だ。最初の1日で全体の方向性と核心的な構造を固め、その後の実装で細部を修正した。重要なのは「後から修正できる設計にしておくこと」だ。ALTER TABLEで追加できる形で設計し、破壊的な変更は最小限に抑えた。
*次回は「最初のプロトタイプが動いた瞬間」*
*シンプルシステム株式会社 代表 伊藤勝彦*