E-commerce Database Schema ER Diagram Example

A useful ER diagram is not just a picture of tables. It should explain how business events move through the database. This e-commerce example shows a compact schema for customers, product catalog, orders, payments, and inventory.

Core Entities

EntityPurposeImportant relationships
usersStores customer accounts and contact information.One user can place many orders.
categoriesGroups products and supports parent-child category trees.One category can contain many products and many child categories.
productsStores product names, pricing, and catalog metadata.One product can appear in many order items and inventory movements.
ordersRepresents a checkout event.One order belongs to one user and has many order items and payments.
order_itemsStores line-item snapshots for quantity and price at purchase time.Each row joins one order to one product.
paymentsTracks payment attempts and settlement status.One order may have multiple payments if retries or split payments are supported.

Example SQL DDL

CREATE TABLE users (
  id BIGINT PRIMARY KEY,
  email VARCHAR(255) NOT NULL UNIQUE,
  display_name VARCHAR(120) NOT NULL,
  created_at TIMESTAMP NOT NULL
);

CREATE TABLE categories (
  id BIGINT PRIMARY KEY,
  parent_id BIGINT NULL,
  name VARCHAR(120) NOT NULL,
  FOREIGN KEY (parent_id) REFERENCES categories(id)
);

CREATE TABLE products (
  id BIGINT PRIMARY KEY,
  category_id BIGINT NOT NULL,
  sku VARCHAR(64) NOT NULL UNIQUE,
  name VARCHAR(200) NOT NULL,
  list_price DECIMAL(10,2) NOT NULL,
  FOREIGN KEY (category_id) REFERENCES categories(id)
);

CREATE TABLE orders (
  id BIGINT PRIMARY KEY,
  user_id BIGINT NOT NULL,
  status VARCHAR(30) NOT NULL,
  ordered_at TIMESTAMP NOT NULL,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE order_items (
  id BIGINT PRIMARY KEY,
  order_id BIGINT NOT NULL,
  product_id BIGINT NOT NULL,
  quantity INT NOT NULL,
  unit_price DECIMAL(10,2) NOT NULL,
  FOREIGN KEY (order_id) REFERENCES orders(id),
  FOREIGN KEY (product_id) REFERENCES products(id)
);

CREATE TABLE payments (
  id BIGINT PRIMARY KEY,
  order_id BIGINT NOT NULL,
  provider VARCHAR(50) NOT NULL,
  status VARCHAR(30) NOT NULL,
  amount DECIMAL(10,2) NOT NULL,
  paid_at TIMESTAMP NULL,
  FOREIGN KEY (order_id) REFERENCES orders(id)
);

How To Read The ER Diagram

The most important relationship is orders to order_items. Keep order item prices as a snapshot instead of reading the current product price later. This preserves historical invoices even when catalog prices change.

The products to categories relationship is many-to-one. If a product can appear in multiple categories, replace the direct category_id with a product_categories join table. That design changes the ER diagram from a simple lookup to a many-to-many relationship.

The payments to orders relationship should usually be one-to-many. Even if your first version only allows a single payment, payment retries, partial captures, refunds, and asynchronous provider callbacks are easier to model when payments are separate rows.

Common Review Checks

Paste the DDL above into Free ER Diagram to generate Mermaid or PlantUML output and compare the relationship layout.