EntityModel.comDocs
Contents

Start here

Guides

Examples

Platforms

Compare

Reference

Learn

E-commerce data model

This example models an online store from browsing to delivery: customers and their saved addresses, a product catalog organized in a category tree, shopping carts, orders with their lines, payments and shipments.

Design notes

  • Orders keep the price that was charged. order_line.unit_price and the totals on the order are stored, not looked up from the product, so later price changes don't rewrite history.
  • Carts and orders are separate. A cart is temporary and can belong to a guest; an order is a permanent record that always has a customer and a shipping address.
  • Categories form a tree through a relationship from category to itself (parent_category_id).
  • Payments are attempts. An order can have several, including failed ones, so the relationship is many-to-one. Card numbers stay with the payment provider; only its reference is stored.
  • Shipments are separate from orders, so one order can ship in several parcels.
  • Status, payment method, country and currency are reference lists with their values, which become lookup tables and foreign keys in SQL.

To extend it: add product_variant for sizes and colors, review for product ratings, promotion and coupon for discounts, or return_request for returns.

Diagram

E-commerce Store: 10 entities, 4 reference lists and 12 relationships. Each box is an entity with its attributes (PK marks the primary key); solid lines are relationships in crow's foot notation, dashed lines point to reference lists.

Address Saved By Customer (N:1)Saved ByCategory Subcategory Of Category (N:1)Subcategory OfProduct Listed In Category (N:1)Listed InCart Belongs To Customer (N:1)Belongs ToCart Item Item In Cart (N:1)Item InCart Item Is For Product (N:1)Is ForOrder Placed By Customer (N:1)Placed ByOrder Ships To Address (N:1)Ships ToOrder Line Line Of Order (N:1)Line OfOrder Line Orders Product (N:1)OrdersPayment Pays For Order (N:1)Pays ForShipment Ships Order (N:1)ShipsCustomer: A person with an account on the store.CustomercustomerPKcustomer_idBIGINTemailSTRINGfull_nameSTRINGphoneSTRINGcreated_atTIMESTAMPmarketing_opt_inBOOLEANAddress: A shipping or billing address saved by a customer.AddressaddressPKaddress_idBIGINTcustomer_idBIGINTaddress_lineSTRINGcitySTRINGpostal_codeSTRINGcountry_code→ countryis_defaultBOOLEANCategory: A node in the product category tree, such as Shoes > Running.CategorycategoryPKcategory_idINTcategory_nameSTRINGparent_category_idINTProduct: An item for sale. Prices are per unit in the store currency.ProductproductPKproduct_idBIGINTskuSTRINGproduct_nameSTRINGdescriptionSTRINGcategory_idINTunit_priceDECIMALcurrency_code→ currencystock_quantityINTis_activeBOOLEANCart: A shopping cart, before checkout.CartcartPKcart_idBIGINTcustomer_idBIGINTcreated_atTIMESTAMPCart Item: A product and quantity in a cart.Cart Itemcart_itemPKcart_item_idBIGINTcart_idBIGINTproduct_idBIGINTquantityINTOrder: A placed order. Totals are stored as charged, so later price changes do not alter history.Ordercustomer_orderPKorder_idBIGINTorder_numberSTRINGcustomer_idBIGINTshipping_address_idBIGINTorder_status_code→ order_statusordered_atTIMESTAMPcurrency_code→ currencysubtotal_amountDECIMALshipping_amountDECIMALtax_amountDECIMALtotal_amountDECIMALOrder Line: One product on an order, with the price charged.Order Lineorder_linePKorder_line_idBIGINTorder_idBIGINTproduct_idBIGINTquantityINTunit_priceDECIMALPayment: A payment attempt against an order. Card details are held by the payment provider, not here.PaymentpaymentPKpayment_idBIGINTorder_idBIGINTpayment_method_code→ payment_methodprovider_referenceSTRINGamountDECIMALpaid_atTIMESTAMPShipment: A parcel sent for an order. Large orders can ship in several parcels.ShipmentshipmentPKshipment_idBIGINTorder_idBIGINTcarrierSTRINGtracking_numberSTRINGshipped_atTIMESTAMPdelivered_atTIMESTAMPOrder Status: The stages an order moves through.Order Statusorder_statusREFERENCEPKstatus_codeSTRINGstatus_nameSTRING6 valuesPayment Method: Ways a customer can pay.Payment Methodpayment_methodREFERENCEPKmethod_codeSTRINGmethod_nameSTRING4 valuesCountry: Countries, identified by their ISO 3166-1 alpha-2 code.CountrycountryREFERENCEPKcountry_codeSTRINGcountry_nameSTRING6 valuesCurrency: Currencies, identified by their ISO 4217 code.CurrencycurrencyREFERENCEPKcurrency_codeSTRINGcurrency_nameSTRING5 values
Open the full-size diagram (SVG)

Open this model in EntityModel.com to explore it, change it and export it. Nothing is uploaded: the model travels in the link. Or download model.json.

Entities

Entity Type Description Primary Key Display Attribute Attributes
Address Entity A shipping or billing address saved by a customer. address_id address_line 7
Cart Entity A shopping cart, before checkout. cart_id created_at 3
Cart Item Entity A product and quantity in a cart. cart_item_id quantity 4
Category Entity A node in the product category tree, such as Shoes > Running. category_id category_name 3
Customer Entity A person with an account on the store. customer_id full_name 6
Order Entity A placed order. Totals are stored as charged, so later price changes do not alter history. order_id order_number 11
Order Line Entity One product on an order, with the price charged. order_line_id unit_price 5
Payment Entity A payment attempt against an order. Card details are held by the payment provider, not here. payment_id provider_reference 6
Product Entity An item for sale. Prices are per unit in the store currency. product_id product_name 9
Shipment Entity A parcel sent for an order. Large orders can ship in several parcels. shipment_id tracking_number 6
Country Reference Countries, identified by their ISO 3166-1 alpha-2 code. country_code country_name 2
Currency Reference Currencies, identified by their ISO 4217 code. currency_code currency_name 2
Order Status Reference The stages an order moves through. status_code status_name 2
Payment Method Reference Ways a customer can pay. method_code method_name 2

Attributes

Address

A shipping or billing address saved by a customer.

Attribute Data Type Flags Reference Example Description
address_id BIGINT PK, Required 5531
customer_id BIGINT Required 100245 The customer who saved the address.
address_line STRING Label, Required, PII 12 Harbour St Street and number.
city STRING Required Portland
postal_code STRING Required, PII 97201
country_code REFERENCE Required → country.country_code US
is_default BOOLEAN Required true The customer's default shipping address.

Cart

A shopping cart, before checkout.

Attribute Data Type Flags Reference Example Description
cart_id BIGINT PK, Required 730021
customer_id BIGINT 100245 Empty for guest carts.
created_at TIMESTAMP Label, Required 2026-10-02T18:40:00Z

Cart Item

A product and quantity in a cart.

Attribute Data Type Flags Reference Example Description
cart_item_id BIGINT PK, Required 991203
cart_id BIGINT Required 730021
product_id BIGINT Required 88012
quantity INT Label, Required 1

Category

A node in the product category tree, such as Shoes > Running.

Attribute Data Type Flags Reference Example Description
category_id INT PK, Required 12
category_name STRING Label, Required Running
parent_category_id INT 3 The parent category; empty for top-level categories.

Customer

A person with an account on the store.

Attribute Data Type Flags Reference Example Description
customer_id BIGINT PK, Required 100245 Surrogate key.
email STRING Required, PII ana@example.com Login and contact email.
full_name STRING Label, Required, PII Ana Silva
phone STRING PII +1 555 0100
created_at TIMESTAMP Required 2026-03-14T10:22:00Z When the account was created.
marketing_opt_in BOOLEAN Required true Consent to marketing email.

Order

A placed order. Totals are stored as charged, so later price changes do not alter history.

Attribute Data Type Flags Reference Example Description
order_id BIGINT PK, Required 5000123
order_number STRING Label, Required SO-2026-000123 Number shown to the customer.
customer_id BIGINT Required 100245
shipping_address_id BIGINT Required 5531
order_status_code REFERENCE Required → order_status.status_code PAID
ordered_at TIMESTAMP Required 2026-10-02T18:52:11Z
currency_code REFERENCE Required → currency.currency_code USD
subtotal_amount DECIMAL(12,2) Required 129.00 Sum of the order lines.
shipping_amount DECIMAL(12,2) Required 7.50
tax_amount DECIMAL(12,2) Required 10.32
total_amount DECIMAL(12,2) Required 146.82 Amount charged.

Order Line

One product on an order, with the price charged.

Attribute Data Type Flags Reference Example Description
order_line_id BIGINT PK, Required 7700451
order_id BIGINT Required 5000123
product_id BIGINT Required 88012
quantity INT Required 1
unit_price DECIMAL(12,2) Label, Required 129.00 Price at the time of the order.

Payment

A payment attempt against an order. Card details are held by the payment provider, not here.

Attribute Data Type Flags Reference Example Description
payment_id BIGINT PK, Required 610045
order_id BIGINT Required 5000123
payment_method_code REFERENCE Required → payment_method.method_code CARD
provider_reference STRING Label pi_3Pq2... The payment provider's transaction ID.
amount DECIMAL(12,2) Required 146.82
paid_at TIMESTAMP 2026-10-02T18:52:15Z Empty until the payment succeeds.

Product

An item for sale. Prices are per unit in the store currency.

Attribute Data Type Flags Reference Example Description
product_id BIGINT PK, Required 88012
sku STRING Required RUN-TRL-42-BLK Stock keeping unit.
product_name STRING Label, Required Trail Runner 42
description STRING Long description shown on the product page.
category_id INT Required 12
unit_price DECIMAL(12,2) Required 129.00 Current list price.
currency_code REFERENCE Required → currency.currency_code USD
stock_quantity INT Required 37 Units available to sell.
is_active BOOLEAN Required true Shown in the store.

Shipment

A parcel sent for an order. Large orders can ship in several parcels.

Attribute Data Type Flags Reference Example Description
shipment_id BIGINT PK, Required 320017
order_id BIGINT Required 5000123
carrier STRING Required UPS
tracking_number STRING Label 1Z999AA10123456784
shipped_at TIMESTAMP 2026-10-03T09:10:00Z
delivered_at TIMESTAMP 2026-10-05T14:31:00Z

Country

Countries, identified by their ISO 3166-1 alpha-2 code.

Attribute Data Type Flags Reference Example Description
country_code STRING PK, Required US ISO 3166-1 alpha-2 code.
country_name STRING Label, Required United States English short name.

Currency

Currencies, identified by their ISO 4217 code.

Attribute Data Type Flags Reference Example Description
currency_code STRING PK, Required USD ISO 4217 code.
currency_name STRING Label, Required US Dollar

Order Status

The stages an order moves through.

Attribute Data Type Flags Reference Example Description
status_code STRING PK, Required PAID
status_name STRING Label, Required Paid

Payment Method

Ways a customer can pay.

Attribute Data Type Flags Reference Example Description
method_code STRING PK, Required CARD
method_name STRING Label, Required Credit or debit card

Relationships

Relationship Source Target Cardinality Forward Label Reverse Label Key Description
saved_by Address Customer N:1 Saved By Saves address.customer_id A customer can save several addresses.
subcategory_of Category Category N:1 Subcategory Of Has Subcategory category.parent_category_id Categories form a tree.
listed_in Product Category N:1 Listed In Lists product.category_id Each product has one primary category.
belongs_to_customer Cart Customer N:1 Belongs To Has Cart cart.customer_id Guest carts have no customer.
item_in Cart Item Cart N:1 Item In Contains cart_item.cart_id
item_product Cart Item Product N:1 Is For In Cart Item cart_item.product_id
placed_by Order Customer N:1 Placed By Places customer_order.customer_id Every order belongs to one customer.
ships_to Order Address N:1 Ships To Receives customer_order.shipping_address_id
line_of Order Line Order N:1 Line Of Has Line order_line.order_id An order has one or more lines.
line_product Order Line Product N:1 Orders Ordered On order_line.product_id
pays_for Payment Order N:1 Pays For Paid By payment.order_id Failed attempts are kept, so an order can have several payments.
ships Shipment Order N:1 Ships Shipped In shipment.order_id

Reference data

Country

6 values · Source: ISO 3166-1 (excerpt)

country_code country_name
US United States
CA Canada
GB United Kingdom
DE Germany
FR France
AU Australia

Currency

5 values · Source: ISO 4217 (excerpt)

currency_code currency_name
USD US Dollar
CAD Canadian Dollar
GBP Pound Sterling
EUR Euro
AUD Australian Dollar

Order Status

6 values · Source: Example values

status_code status_name
PENDING Pending payment
PAID Paid
SHIPPED Shipped
DELIVERED Delivered
CANCELLED Cancelled
REFUNDED Refunded

Payment Method

4 values · Source: Example values

method_code method_name
CARD Credit or debit card
PAYPAL PayPal
APPLE_PAY Apple Pay
GIFT_CARD Gift card

SQL DDL (PostgreSQL)

Generated from the model. In the app, Export → Code also writes MySQL, Snowflake and Databricks DDL, DBML, dbt schema.yml and data contracts.

-- E-commerce Store — data model
-- PostgreSQL DDL, generated 2026-11-03
-- Made with EntityModel.com

-- Reference list: Order Status
CREATE TABLE order_status (
  status_code TEXT NOT NULL,
  status_name TEXT NOT NULL,
  PRIMARY KEY (status_code)
);
COMMENT ON TABLE order_status IS 'The stages an order moves through.';

-- Reference list: Payment Method
CREATE TABLE payment_method (
  method_code TEXT NOT NULL,
  method_name TEXT NOT NULL,
  PRIMARY KEY (method_code)
);
COMMENT ON TABLE payment_method IS 'Ways a customer can pay.';

-- Reference list: Country
CREATE TABLE country (
  country_code TEXT NOT NULL,
  country_name TEXT NOT NULL,
  PRIMARY KEY (country_code)
);
COMMENT ON TABLE country IS 'Countries, identified by their ISO 3166-1 alpha-2 code.';
COMMENT ON COLUMN country.country_code IS 'ISO 3166-1 alpha-2 code.';
COMMENT ON COLUMN country.country_name IS 'English short name.';

-- Reference list: Currency
CREATE TABLE currency (
  currency_code TEXT NOT NULL,
  currency_name TEXT NOT NULL,
  PRIMARY KEY (currency_code)
);
COMMENT ON TABLE currency IS 'Currencies, identified by their ISO 4217 code.';
COMMENT ON COLUMN currency.currency_code IS 'ISO 4217 code.';

CREATE TABLE customer (
  customer_id BIGINT NOT NULL,
  email TEXT NOT NULL,
  full_name TEXT NOT NULL,
  phone TEXT,
  created_at TIMESTAMP NOT NULL,
  marketing_opt_in BOOLEAN NOT NULL,
  PRIMARY KEY (customer_id)
);
COMMENT ON TABLE customer IS 'A person with an account on the store.';
COMMENT ON COLUMN customer.customer_id IS 'Surrogate key.';
COMMENT ON COLUMN customer.email IS 'Login and contact email.';
COMMENT ON COLUMN customer.created_at IS 'When the account was created.';
COMMENT ON COLUMN customer.marketing_opt_in IS 'Consent to marketing email.';

CREATE TABLE address (
  address_id BIGINT NOT NULL,
  customer_id BIGINT NOT NULL,
  address_line TEXT NOT NULL,
  city TEXT NOT NULL,
  postal_code TEXT NOT NULL,
  country_code TEXT NOT NULL,
  is_default BOOLEAN NOT NULL,
  PRIMARY KEY (address_id)
);
COMMENT ON TABLE address IS 'A shipping or billing address saved by a customer.';
COMMENT ON COLUMN address.customer_id IS 'The customer who saved the address.';
COMMENT ON COLUMN address.address_line IS 'Street and number.';
COMMENT ON COLUMN address.is_default IS 'The customer''s default shipping address.';

CREATE TABLE category (
  category_id INTEGER NOT NULL,
  category_name TEXT NOT NULL,
  parent_category_id INTEGER,
  PRIMARY KEY (category_id)
);
COMMENT ON TABLE category IS 'A node in the product category tree, such as Shoes > Running.';
COMMENT ON COLUMN category.parent_category_id IS 'The parent category; empty for top-level categories.';

CREATE TABLE product (
  product_id BIGINT NOT NULL,
  sku TEXT NOT NULL,
  product_name TEXT NOT NULL,
  description TEXT,
  category_id INTEGER NOT NULL,
  unit_price NUMERIC(12,2) NOT NULL,
  currency_code TEXT NOT NULL,
  stock_quantity INTEGER NOT NULL,
  is_active BOOLEAN NOT NULL,
  PRIMARY KEY (product_id)
);
COMMENT ON TABLE product IS 'An item for sale. Prices are per unit in the store currency.';
COMMENT ON COLUMN product.sku IS 'Stock keeping unit.';
COMMENT ON COLUMN product.description IS 'Long description shown on the product page.';
COMMENT ON COLUMN product.unit_price IS 'Current list price.';
COMMENT ON COLUMN product.stock_quantity IS 'Units available to sell.';
COMMENT ON COLUMN product.is_active IS 'Shown in the store.';

CREATE TABLE cart (
  cart_id BIGINT NOT NULL,
  customer_id BIGINT,
  created_at TIMESTAMP NOT NULL,
  PRIMARY KEY (cart_id)
);
COMMENT ON TABLE cart IS 'A shopping cart, before checkout.';
COMMENT ON COLUMN cart.customer_id IS 'Empty for guest carts.';

CREATE TABLE cart_item (
  cart_item_id BIGINT NOT NULL,
  cart_id BIGINT NOT NULL,
  product_id BIGINT NOT NULL,
  quantity INTEGER NOT NULL,
  PRIMARY KEY (cart_item_id)
);
COMMENT ON TABLE cart_item IS 'A product and quantity in a cart.';

CREATE TABLE customer_order (
  order_id BIGINT NOT NULL,
  order_number TEXT NOT NULL,
  customer_id BIGINT NOT NULL,
  shipping_address_id BIGINT NOT NULL,
  order_status_code TEXT NOT NULL,
  ordered_at TIMESTAMP NOT NULL,
  currency_code TEXT NOT NULL,
  subtotal_amount NUMERIC(12,2) NOT NULL,
  shipping_amount NUMERIC(12,2) NOT NULL,
  tax_amount NUMERIC(12,2) NOT NULL,
  total_amount NUMERIC(12,2) NOT NULL,
  PRIMARY KEY (order_id)
);
COMMENT ON TABLE customer_order IS 'A placed order. Totals are stored as charged, so later price changes do not alter history.';
COMMENT ON COLUMN customer_order.order_number IS 'Number shown to the customer.';
COMMENT ON COLUMN customer_order.subtotal_amount IS 'Sum of the order lines.';
COMMENT ON COLUMN customer_order.total_amount IS 'Amount charged.';

CREATE TABLE order_line (
  order_line_id BIGINT NOT NULL,
  order_id BIGINT NOT NULL,
  product_id BIGINT NOT NULL,
  quantity INTEGER NOT NULL,
  unit_price NUMERIC(12,2) NOT NULL,
  PRIMARY KEY (order_line_id)
);
COMMENT ON TABLE order_line IS 'One product on an order, with the price charged.';
COMMENT ON COLUMN order_line.unit_price IS 'Price at the time of the order.';

CREATE TABLE payment (
  payment_id BIGINT NOT NULL,
  order_id BIGINT NOT NULL,
  payment_method_code TEXT NOT NULL,
  provider_reference TEXT,
  amount NUMERIC(12,2) NOT NULL,
  paid_at TIMESTAMP,
  PRIMARY KEY (payment_id)
);
COMMENT ON TABLE payment IS 'A payment attempt against an order. Card details are held by the payment provider, not here.';
COMMENT ON COLUMN payment.provider_reference IS 'The payment provider''s transaction ID.';
COMMENT ON COLUMN payment.paid_at IS 'Empty until the payment succeeds.';

CREATE TABLE shipment (
  shipment_id BIGINT NOT NULL,
  order_id BIGINT NOT NULL,
  carrier TEXT NOT NULL,
  tracking_number TEXT,
  shipped_at TIMESTAMP,
  delivered_at TIMESTAMP,
  PRIMARY KEY (shipment_id)
);
COMMENT ON TABLE shipment IS 'A parcel sent for an order. Large orders can ship in several parcels.';

-- Keys between tables
ALTER TABLE customer ADD CONSTRAINT uq_customer_email UNIQUE (email);
ALTER TABLE product ADD CONSTRAINT uq_product_sku UNIQUE (sku);
ALTER TABLE customer_order ADD CONSTRAINT uq_customer_order_order_number UNIQUE (order_number);
ALTER TABLE address ADD CONSTRAINT fk_address_country_code FOREIGN KEY (country_code) REFERENCES country (country_code);
ALTER TABLE product ADD CONSTRAINT fk_product_currency_code FOREIGN KEY (currency_code) REFERENCES currency (currency_code);
ALTER TABLE customer_order ADD CONSTRAINT fk_customer_order_order_status_code FOREIGN KEY (order_status_code) REFERENCES order_status (status_code);
ALTER TABLE customer_order ADD CONSTRAINT fk_customer_order_currency_code FOREIGN KEY (currency_code) REFERENCES currency (currency_code);
ALTER TABLE payment ADD CONSTRAINT fk_payment_payment_method_code FOREIGN KEY (payment_method_code) REFERENCES payment_method (method_code);
ALTER TABLE address ADD CONSTRAINT fk_saved_by FOREIGN KEY (customer_id) REFERENCES customer (customer_id);
ALTER TABLE category ADD CONSTRAINT fk_subcategory_of FOREIGN KEY (parent_category_id) REFERENCES category (category_id);
ALTER TABLE product ADD CONSTRAINT fk_listed_in FOREIGN KEY (category_id) REFERENCES category (category_id);
ALTER TABLE cart ADD CONSTRAINT fk_belongs_to_customer FOREIGN KEY (customer_id) REFERENCES customer (customer_id);
ALTER TABLE cart_item ADD CONSTRAINT fk_item_in FOREIGN KEY (cart_id) REFERENCES cart (cart_id);
ALTER TABLE cart_item ADD CONSTRAINT fk_item_product FOREIGN KEY (product_id) REFERENCES product (product_id);
ALTER TABLE customer_order ADD CONSTRAINT fk_placed_by FOREIGN KEY (customer_id) REFERENCES customer (customer_id);
ALTER TABLE customer_order ADD CONSTRAINT fk_ships_to FOREIGN KEY (shipping_address_id) REFERENCES address (address_id);
ALTER TABLE order_line ADD CONSTRAINT fk_line_of FOREIGN KEY (order_id) REFERENCES customer_order (order_id);
ALTER TABLE order_line ADD CONSTRAINT fk_line_product FOREIGN KEY (product_id) REFERENCES product (product_id);
ALTER TABLE payment ADD CONSTRAINT fk_pays_for FOREIGN KEY (order_id) REFERENCES customer_order (order_id);
ALTER TABLE shipment ADD CONSTRAINT fk_ships FOREIGN KEY (order_id) REFERENCES customer_order (order_id);

-- Reference values
INSERT INTO order_status (status_code, status_name) VALUES
  ('PENDING', 'Pending payment'),
  ('PAID', 'Paid'),
  ('SHIPPED', 'Shipped'),
  ('DELIVERED', 'Delivered'),
  ('CANCELLED', 'Cancelled'),
  ('REFUNDED', 'Refunded');

INSERT INTO payment_method (method_code, method_name) VALUES
  ('CARD', 'Credit or debit card'),
  ('PAYPAL', 'PayPal'),
  ('APPLE_PAY', 'Apple Pay'),
  ('GIFT_CARD', 'Gift card');

INSERT INTO country (country_code, country_name) VALUES
  ('US', 'United States'),
  ('CA', 'Canada'),
  ('GB', 'United Kingdom'),
  ('DE', 'Germany'),
  ('FR', 'France'),
  ('AU', 'Australia');

INSERT INTO currency (currency_code, currency_name) VALUES
  ('USD', 'US Dollar'),
  ('CAD', 'Canadian Dollar'),
  ('GBP', 'Pound Sterling'),
  ('EUR', 'Euro'),
  ('AUD', 'Australian Dollar');

Mermaid

Paste into a mermaid code block on GitHub, GitLab or Notion to show the diagram there.

---
title: E-commerce Store
---
%% Made with EntityModel.com
erDiagram
  customer {
    bigint customer_id PK "Surrogate key."
    string email "Login and contact email."
    string full_name "Full Name"
    string phone "Phone"
    timestamp created_at "When the account was created."
    boolean marketing_opt_in "Consent to marketing email."
  }
  address {
    bigint address_id PK "Address ID"
    bigint customer_id FK "The customer who saved the address."
    string address_line "Street and number."
    string city "City"
    string postal_code "Postal Code"
    string country_code FK "Country Code"
    boolean is_default "The customer's default shipping address."
  }
  category {
    int category_id PK "Category ID"
    string category_name "Category Name"
    int parent_category_id FK "The parent category; empty for top-level categories."
  }
  product {
    bigint product_id PK "Product ID"
    string sku "Stock keeping unit."
    string product_name "Product Name"
    string description "Long description shown on the product page."
    int category_id FK "Category ID"
    decimal unit_price "Current list price."
    string currency_code FK "Currency Code"
    int stock_quantity "Units available to sell."
    boolean is_active "Shown in the store."
  }
  cart {
    bigint cart_id PK "Cart ID"
    bigint customer_id FK "Empty for guest carts."
    timestamp created_at "Created At"
  }
  cart_item {
    bigint cart_item_id PK "Cart Item ID"
    bigint cart_id FK "Cart ID"
    bigint product_id FK "Product ID"
    int quantity "Quantity"
  }
  customer_order {
    bigint order_id PK "Order ID"
    string order_number "Number shown to the customer."
    bigint customer_id FK "Customer ID"
    bigint shipping_address_id FK "Shipping Address ID"
    string order_status_code FK "Order Status Code"
    timestamp ordered_at "Ordered At"
    string currency_code FK "Currency Code"
    decimal subtotal_amount "Sum of the order lines."
    decimal shipping_amount "Shipping Amount"
    decimal tax_amount "Tax Amount"
    decimal total_amount "Amount charged."
  }
  order_line {
    bigint order_line_id PK "Order Line ID"
    bigint order_id FK "Order ID"
    bigint product_id FK "Product ID"
    int quantity "Quantity"
    decimal unit_price "Price at the time of the order."
  }
  payment {
    bigint payment_id PK "Payment ID"
    bigint order_id FK "Order ID"
    string payment_method_code FK "Payment Method Code"
    string provider_reference "The payment provider's transaction ID."
    decimal amount "Amount"
    timestamp paid_at "Empty until the payment succeeds."
  }
  shipment {
    bigint shipment_id PK "Shipment ID"
    bigint order_id FK "Order ID"
    string carrier "Carrier"
    string tracking_number "Tracking Number"
    timestamp shipped_at "Shipped At"
    timestamp delivered_at "Delivered At"
  }
  order_status {
    string status_code PK "Status Code"
    string status_name "Status Name"
  }
  payment_method {
    string method_code PK "Method Code"
    string method_name "Method Name"
  }
  country {
    string country_code PK "ISO 3166-1 alpha-2 code."
    string country_name "English short name."
  }
  currency {
    string currency_code PK "ISO 4217 code."
    string currency_name "Currency Name"
  }
  address }o--|| customer : "Saved By"
  category }o--|| category : "Subcategory Of"
  product }o--|| category : "Listed In"
  cart }o--|| customer : "Belongs To"
  cart_item }o--|| cart : "Item In"
  cart_item }o--|| product : "Is For"
  customer_order }o--|| customer : "Placed By"
  customer_order }o--|| address : "Ships To"
  order_line }o--|| customer_order : "Line Of"
  order_line }o--|| product : "Orders"
  payment }o--|| customer_order : "Pays For"
  shipment }o--|| customer_order : "Ships"
  address }o..|| country : "Country Code"
  product }o..|| currency : "Currency Code"
  customer_order }o..|| order_status : "Order Status Code"
  customer_order }o..|| currency : "Currency Code"
  payment }o..|| payment_method : "Payment Method Code"

Frequently asked questions

What tables does an e-commerce database need?

At minimum: customer, address, product, category, order (often named customer_order, since ORDER is a reserved word), order line, payment and shipment, plus lookup tables for order status and payment method. A cart and cart item support saved baskets before checkout.

Why is the order table called customer_order?

ORDER is a reserved word in SQL, so naming the table customer_order avoids quoting it in every query. The display name is still Order.

How do I store prices that change over time?

Store the price actually charged on each order line. For a full price history of the catalog, add a product_price table with effective-from and effective-to dates.