🚀 コードの保守性には気を遣うけど、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
);
🎯 要件整理のポイント
- 現在の必須要件を明確にする
 - 将来の拡張可能性を考慮する
 - 「念のため」フィールドは避ける
 - 実際の使用パターンを想定する
 
🌟 まとめ
DB設計は、一度決めてしまうと後から変更するのが非常に困難です。だからこそ、最初の設計段階でしっかりと検討することが重要。
🎯 DB設計で意識すべきポイント
- ✅ 要件を深く理解する
 - ✅ 正規化で無駄と重複を排除する
 - ✅ 将来のスケールを考慮する
 - ✅ 本当に必要な機能だけを実装する
 - ✅ データの整合性を保つ仕組みを作る
 
コードのリファクタリングと同じように、DB設計にも時間と労力を投資することで、長期的に保守しやすく、スケールしやすいシステムを構築できます。
皆さんも、次のプロジェクトではDB設計により多くの時間を割いて、後から「あの時ちゃんと設計しておけばよかった...」と後悔しないような設計を心がけていきましょう!
🚀 良いDB設計で、より良いアプリケーション開発を!