ECサイトの設計例
🛒 ECサイトの設計例
Section titled “🛒 ECサイトの設計例”実際のECサイトを例に、API設計、テーブル設計、トランザクション設計を説明します。
📋 要件定義
Section titled “📋 要件定義”✅ 機能要件
Section titled “✅ 機能要件”- 👤 ユーザー管理(登録、ログイン、プロフィール)
- 📦 商品管理(商品一覧、商品詳細、検索)
- 🛒 注文管理(カート、注文、決済)
- 📊 在庫管理(在庫確認、在庫更新)
🌐 API設計
Section titled “🌐 API設計”👤 1. ユーザーAPI
Section titled “👤 1. ユーザー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"}2. 商品API
Section titled “2. 商品API”// 商品一覧GET /api/v1/products?page=1&limit=20&category=electronics
// 商品詳細GET /api/v1/products/:id
// 商品検索GET /api/v1/products/search?q=laptop3. 注文API
Section titled “3. 注文API”// カート追加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"}テーブル設計
Section titled “テーブル設計”1. ユーザーテーブル
Section titled “1. ユーザーテーブル”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));2. 商品テーブル
Section titled “2. 商品テーブル”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));3. 注文テーブル
Section titled “3. 注文テーブル”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));トランザクション設計
Section titled “トランザクション設計”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;2. エラーハンドリング
Section titled “2. エラーハンドリング”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(); }}パフォーマンス最適化
Section titled “パフォーマンス最適化”1. インデックス設計
Section titled “1. インデックス設計”-- よく検索されるカラムにインデックス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);2. キャッシング
Section titled “2. キャッシング”// 商品一覧のキャッシュ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サイトを構築できます。