後悔しないDB設計を考える。あとから変えにくいから最初に見ること

後悔しないDB設計を考える。あとから変えにくいから最初に見ることの説明画像

コードの保守性には気を遣うけど、DB設計は?

開発をしていると、「コードの保守性」というキーワードをよく聞きます。

リファクタリングの重要性、可読性の向上、設計パターンの適用など、コード品質に関しては多くの開発者が意識を向けています。

DB設計はどうでしょうか?

今回は、なぜDB設計が重要なのか、そして実際にどう取り組めばよいのかを整理してみました。

コードは変えられるが、DBは変えにくい

コードはリファクタリングできる

# 修正前:読みにくいコード
def calc(a, b, c)
  if a > 0
    return a * b + c
  else
    return 0
  end
end

# 修正後:読みやすいコード
def calculate_total_price(quantity, unit_price, tax)
  return 0 if quantity <= 0

  quantity * unit_price + tax
end

コードの場合、このようにリファクタリングという形で後から改善できます。変数名を変更したり、関数を分割したり、設計パターンを適用したりと、運用中でも比較的安全に修正が可能です。

DBは運用開始後の変更が困難

一方で、DB設計は運用が始まってから変更するのは結構難しいんです。

-- 後から気づいた問題のあるテーブル設計
CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(255),
  email VARCHAR(255),
  address TEXT  -- 住所を1つのフィールドに格納
);

-- 理想的だった設計
CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(255),
  email VARCHAR(255)
);

CREATE TABLE addresses (
  id INT PRIMARY KEY,
  user_id INT,
  prefecture VARCHAR(50),
  city VARCHAR(100),
  street VARCHAR(255),
  postal_code VARCHAR(10),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

上記の例で、運用開始後に住所の検索機能が必要になった場合、都道府県別の集計が必要になった場合など、既存のデータがある状態でのテーブル構造変更は:

  • データ移行が必要
  • ダウンタイムが発生する可能性
  • アプリケーションコードの大幅な修正が必要
  • バックアップとロールバック計画が必須

スケールしやすい構造を最初から考える

要件からテーブル・カラム設計を考える

DB設計では、しっかりと要件から何をテーブルとして、何をカラムとして持つのかを考える必要があります。

-- これで本当に十分?
CREATE TABLE products (
  id INT PRIMARY KEY,
  name VARCHAR(255),
  price DECIMAL(10,2),
  category VARCHAR(100)
);

-- 将来を見据えた設計
CREATE TABLE categories (
  id INT PRIMARY KEY,
  name VARCHAR(100),
  parent_id INT,
  FOREIGN KEY (parent_id) REFERENCES categories(id)
);

CREATE TABLE products (
  id INT PRIMARY KEY,
  name VARCHAR(255),
  description TEXT,
  category_id INT,
  created_at TIMESTAMP,
  updated_at TIMESTAMP,
  FOREIGN KEY (category_id) REFERENCES categories(id)
);

CREATE TABLE product_prices (
  id INT PRIMARY KEY,
  product_id INT,
  price DECIMAL(10,2),
  effective_from DATE,
  effective_to DATE,
  FOREIGN KEY (product_id) REFERENCES products(id)
);

この設計により:
- カテゴリの階層構造に対応
- 価格履歴の管理が可能
- 将来的な拡張に対応しやすい

正規化:「無駄を省くこと」

正規化は難しくない(難しいけど...)

よく言われるのは正規化ですが、一言でいえば「無駄を省くこと」です。何も難しいことではなく、無駄や重複、論理破綻が起きないように設計していけばよいのです。

-- 非正規化:データの重複と不整合のリスク
CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_name VARCHAR(255),
  customer_email VARCHAR(255),
  customer_address TEXT,
  product_name VARCHAR(255),
  product_price DECIMAL(10,2),
  quantity INT
);

-- 正規化:データの重複を排除
CREATE TABLE customers (
  id INT PRIMARY KEY,
  name VARCHAR(255),
  email VARCHAR(255),
  address TEXT
);

CREATE TABLE products (
  id INT PRIMARY KEY,
  name VARCHAR(255),
  price DECIMAL(10,2)
);

CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT,
  order_date DATE,
  FOREIGN KEY (customer_id) REFERENCES customers(id)
);

CREATE TABLE order_items (
  id INT PRIMARY KEY,
  order_id INT,
  product_id INT,
  quantity INT,
  FOREIGN KEY (order_id) REFERENCES orders(id),
  FOREIGN KEY (product_id) REFERENCES products(id)
);

正規化のメリット

  • データの不整合を防ぐ
  • ストレージの効率化
  • 更新処理の単純化
  • データの整合性担保

本当に必要な要件かを考える

無駄を作らないために

無駄を作らないためにも、本当に必要な要件なのかをしっかり考えることが大切です。

-- 「念のため」で追加したフィールド
CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(255),
  email VARCHAR(255),
  phone VARCHAR(20),
  fax VARCHAR(20),        -- 本当に必要?
  blood_type VARCHAR(5),  -- 本当に必要?
  hobby TEXT,            -- 本当に必要?
  memo TEXT              -- 何のためのメモ?
);

-- 必要最小限から始める
CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL UNIQUE,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

要件整理のポイント

  1. 現在の必須要件を明確にする
  2. 将来の拡張可能性を考慮する
  3. 「念のため」フィールドは避ける
  4. 実際の使用パターンを想定する

まとめ

DB設計は、一度決めてしまうと後から変更するのが非常に困難です。だからこそ、最初の設計段階でしっかりと検討することが重要。

DB設計で意識すべきポイント

  • 要件を深く理解する
  • 正規化で無駄と重複を排除する
  • 将来のスケールを考慮する
  • 本当に必要な機能だけを実装する
  • データの整合性を保つ仕組みを作る

コードのリファクタリングと同じように、DB設計にも時間と労力を投資することで、長期的に保守しやすく、スケールしやすいシステムを構築できます。

皆さんも、次のプロジェクトではDB設計により多くの時間を割いて、後から「あの時ちゃんと設計しておけばよかった...」と後悔しないような設計を心がけていきましょう!

良いDB設計で、より良いアプリケーション開発を!

最初に考えるべきなのは「今の画面」だけではない

DB設計で怖いのは、今の画面だけを見ると問題なさそうに見えることです。

例えば、画面上では住所が1つ表示できればよい。
だから address カラムを1つ置けばよい。

その判断は、最初は自然に見えます。

ただ、あとから次のような要件が出ると急に苦しくなります。

  • 複数住所を持ちたい
  • 都道府県で検索したい
  • 配送先と請求先を分けたい
  • 住所変更履歴を残したい
  • 外部サービスへ住所情報を連携したい

DB設計では、今の入力フォームだけではなく、データがどう使われるかまで見る必要があります。

画像をDBに保存するという選択 でも近い話がありますが、保存場所や持ち方は、あとから運用や拡張にかなり影響します。

判断に迷ったときのチェックリスト

自分なら、設計前に最低限このあたりを確認したいです。

- このデータは増えるか
- 履歴を持つ必要があるか
- 検索条件になるか
- 集計に使うか
- 外部サービスと連携するか
- 削除してよいデータか
- 後から分割すると移行が大変か

特に「検索条件になるか」「履歴を持つか」は、あとから効いてくることが多いです。

DB設計は、未来を完全に当てる作業ではありません。
ただ、変わりそうな場所を少し見ておくだけで、後悔はかなり減らせます。

最初から完璧にするのではなく、変化に弱い持ち方を避ける。
そのくらいの現実的な姿勢が、実務では大事なのだと思います。

この記事をシェア