Skip to content

ECサイトの設計例

実際のECサイトを例に、API設計テーブル設計トランザクション設計を説明します。

  • 👤 ユーザー管理(登録、ログイン、プロフィール)
  • 📦 商品管理(商品一覧、商品詳細、検索)
  • 🛒 注文管理(カート、注文、決済)
  • 📊 在庫管理(在庫確認、在庫更新)
// ユーザー登録
POST /api/v1/users
{
"username": "john_doe",
"email": "john@example.com",
"password": "password123"
}
// ユーザー取得
GET /api/v1/users/:id
// ユーザー更新
PUT /api/v1/users/:id
{
"name": "John Doe",
"email": "john@example.com"
}
// 商品一覧
GET /api/v1/products?page=1&limit=20&category=electronics
// 商品詳細
GET /api/v1/products/:id
// 商品検索
GET /api/v1/products/search?q=laptop
// カート追加
POST /api/v1/cart/items
{
"product_id": 1,
"quantity": 2
}
// 注文作成
POST /api/v1/orders
{
"items": [
{ "product_id": 1, "quantity": 2 },
{ "product_id": 2, "quantity": 1 }
],
"shipping_address": "...",
"payment_method": "credit_card"
}
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
first_name VARCHAR(100),
last_name VARCHAR(100),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_email (email),
INDEX idx_username (username)
);
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL,
stock_quantity INT NOT NULL DEFAULT 0,
category_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (category_id) REFERENCES categories(id),
INDEX idx_category (category_id),
INDEX idx_name (name)
);
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
total_amount DECIMAL(10,2) NOT NULL,
shipping_address TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
INDEX idx_user_id (user_id),
INDEX idx_status (status),
INDEX idx_created_at (created_at)
);
CREATE TABLE order_items (
id INT PRIMARY KEY AUTO_INCREMENT,
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL,
price DECIMAL(10,2) NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id),
INDEX idx_order_id (order_id),
INDEX idx_product_id (product_id)
);

1. 注文処理のトランザクション

Section titled “1. 注文処理のトランザクション”
-- 注文処理
BEGIN TRANSACTION;
-- 在庫を確認・ロック
SELECT stock_quantity FROM products WHERE id = 1 FOR UPDATE;
-- 在庫が十分か確認
-- (アプリケーション側で確認)
-- 在庫を減らす
UPDATE products SET stock_quantity = stock_quantity - 2 WHERE id = 1;
-- 注文を作成
INSERT INTO orders (user_id, status, total_amount, shipping_address)
VALUES (1, 'pending', 200.00, '...');
SET @order_id = LAST_INSERT_ID();
-- 注文明細を作成
INSERT INTO order_items (order_id, product_id, quantity, price)
VALUES (@order_id, 1, 2, 100.00);
COMMIT;
async function createOrder(userId, items, shippingAddress) {
const connection = await db.getConnection();
try {
await connection.beginTransaction();
// 在庫確認とロック
for (const item of items) {
const [product] = await connection.query(
'SELECT stock_quantity FROM products WHERE id = ? FOR UPDATE',
[item.product_id]
);
if (product.stock_quantity < item.quantity) {
throw new Error('Insufficient stock');
}
}
// 在庫を減らす
for (const item of items) {
await connection.query(
'UPDATE products SET stock_quantity = stock_quantity - ? WHERE id = ?',
[item.quantity, item.product_id]
);
}
// 注文を作成
const [order] = await connection.query(
'INSERT INTO orders (user_id, status, total_amount, shipping_address) VALUES (?, ?, ?, ?)',
[userId, 'pending', calculateTotal(items), shippingAddress]
);
// 注文明細を作成
for (const item of items) {
await connection.query(
'INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (?, ?, ?, ?)',
[order.insertId, item.product_id, item.quantity, item.price]
);
}
await connection.commit();
return order;
} catch (error) {
await connection.rollback();
throw error;
} finally {
connection.release();
}
}
-- よく検索されるカラムにインデックス
CREATE INDEX idx_products_category ON products(category_id);
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
CREATE INDEX idx_order_items_order ON order_items(order_id);
// 商品一覧のキャッシュ
async function getProducts(categoryId, page, limit) {
const cacheKey = `products:${categoryId}:${page}:${limit}`;
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
const products = await db.query(`
SELECT * FROM products
WHERE category_id = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?
`, [categoryId, limit, (page - 1) * limit]);
await redis.setex(cacheKey, 3600, JSON.stringify(products));
return products;
}

ECサイトの設計例:

  • API設計: RESTful API、適切なエラーハンドリング
  • テーブル設計: 正規化、適切なインデックス
  • トランザクション設計: ACID特性、エラーハンドリング
  • パフォーマンス最適化: インデックス、キャッシング

この設計例を参考に、実際のECサイトを構築できます。