By Bartłomiej Adamiec
The database serves as the core of an e-commerce platform. It is designed to manage the entire lifecycle of online product sales, ensuring data integrity and the operational efficiency of the online store.
- Customers: Users who browse the catalog and place orders.
- Warehouses: Physical locations where product inventory is stored.
- Products & Categories: A catalog of goods available for sale, along with their descriptions and categorization.
- Addresses: A centralized address system for both customers and warehouses.
- Carts & Orders: Transactional data (shopping sessions and finalized contracts).
- Sensitive Payment Details: Raw credit card numbers or CVV codes. It only records the status of payments and the provider used.
- Employee Data: Information regarding HR structure, salaries, or staff schedules is not included.
- Marketing Analytics: User behavior on the website, such as clickstreams, page views, or ad campaign performance.
The system is designed to support two main roles: Customers and Administrators.
Customers can:
- Create accounts and manage their personal data and address book (shipping/billing).
- Browse products by category and search for items by name.
- Add products to the cart, modify quantity, or remove items before purchase.
- Place orders (convert cart to order) and pay for them.
- Check order history and current status (e.g., "Shipped").
Administrators can:
- Manage the product catalog: add new items, update descriptions, change prices, and assign categories.
- Monitor stock levels across various locations (warehouses).
- Handle order fulfillment: change order and payment statuses.
- Use analytical views (e.g.,
logistics) to generate picking lists for warehouse staff or resolve customer issues.
In my opinion, the database can be extended in the future to include the following functionalities:
- Reviews & Ratings: Ability to add customer reviews on exact products and add an overall rating on a scale of 1 to 10.
- Multi-currency Support: All prices are currently stored in a single currency; there is no dynamic exchange rate conversion mechanism.
- Advanced Returns (RMA): While an order can have a 'Cancelled' status, there is no dedicated system for handling partial returns or warranty claims.
The database includes the following entities:
-
id, which specifies the unique ID for the address as anINTEGER. This column has thePRIMARY KEYconstraint applied. -
country, which is the full country name in English, set asTEXT. TheDEFAULTvalue is set to 'Poland'. -
city, which is the full city name in English, set asTEXT. -
zip_code, which is set asTEXTbecause different countries have different zip code styles. -
street, which is the full street name including the building number, set asTEXT. -
apartment, which is the apartment number, set asTEXTbecause some buildings have alphabetical suffixes.
Not all columns in this table have the NOT NULL constraint because not every address requires an apartment number (e.g., detached houses).
-
id, which specifies the unique ID for the category as anINTEGER. This column has thePRIMARY KEYconstraint applied. -
name, which is the name of the product category, set asTEXT. This column has theUNIQUEconstraint applied. -
description, which provides details about the category content, set asTEXT.
All columns in this table have the NOT NULL constraint applied. Descriptions are valuable for proper product categorization.
-
id, which specifies the unique ID for the product as anINTEGER. This column has thePRIMARY KEYconstraint applied. -
name, which is the marketing name of the product, set asTEXTwith aUNIQUEconstraint applied to avoid redundancy. -
description, which contains detailed product features, set asTEXT. -
price, which represents the current cost of the product. It is set asDECIMAL(10, 2)to ensure financial precision. -
category_id, which is aFOREIGN KEYreferencing thecategoriestable.
The description and category_id columns do not have the NOT NULL constraint. This allows for new products that might not fit into existing categories immediately or whose descriptions can be added later (optional).
-
id, which specifies the unique ID for the warehouse as anINTEGER. This column has thePRIMARY KEYconstraint applied. -
name, which is the internal name or code of the warehouse, set asTEXT. This is treated optionally. -
address_id, which is aFOREIGN KEYreferencing theaddressestable, specifying the physical location. It has theNOT NULLconstraint applied as we must know the exact location.
-
product_id, which is aFOREIGN KEYreferencing theproductstable. -
warehouse_id, which is aFOREIGN KEYreferencing thewarehousestable. -
stock_level, which indicates the quantity of the product stored at a specific warehouse. It is set asINTEGERwith aDEFAULTof 0 and aCHECKconstraint ensuring non-negative values.
The combination of product_id and warehouse_id forms a composite PRIMARY KEY to ensure a unique record for each product-warehouse pair.
-
id, which specifies the unique ID for the customer as anINTEGER. This column has thePRIMARY KEYconstraint applied. -
first_name, which is the customer's first name, set asTEXT. -
last_name, which is the customer's surname, set asTEXT. -
email, which is the user's email address used for login. It is set asTEXTand has aUNIQUEconstraint. -
password, which stores the hashed password for security, set asTEXT. -
phone_number, which is the contact number with the country prefix, set asTEXT. -
birth_date, which is the date of birth, set asDATE.
Only birth_date in this table is optional. All other columns have the NOT NULL constraint applied.
-
customer_id, which is aFOREIGN KEYreferencing thecustomerstable. -
address_id, which is aFOREIGN KEYreferencing theaddressestable. -
address_type, which specifies the role of the address. It is set asTEXTwith aCHECKconstraint allowing only 'BILLING' or 'SHIPPING'.
The combination of customer_id and address_id forms a composite PRIMARY KEY. All columns have the NOT NULL constraint applied.
-
id, which specifies the unique ID for the cart as anINTEGER. This column has thePRIMARY KEYconstraint applied. -
customer_id, which is aFOREIGN KEYreferencing thecustomerstable with theNOT NULLconstraint applied. -
created_at, which stores the timestamp of cart creation, set asDATETIMEwith aDEFAULTvalue of the current time.
-
id, which specifies the unique ID for the cart line item as anINTEGER. This column has thePRIMARY KEYconstraint applied. -
cart_id, which is aFOREIGN KEYreferencing thecartstable. -
product_id, which is aFOREIGN KEYreferencing theproductstable. -
quantity, which represents the number of units in the cart. It is set asINTEGERwith aCHECKconstraint ensuring the value is greater than 0.
All columns have the NOT NULL constraint applied.
-
id, which specifies the unique ID for the order as anINTEGER. This column has thePRIMARY KEYconstraint applied. -
customer_id, which is aFOREIGN KEYreferencing thecustomerstable. -
shipping_address_id, which is aFOREIGN KEYreferencing theaddressestable. -
total_amount, which is the final sum to be paid, set asDECIMAL(10, 2). -
status, which tracks the order progress. It is set asTEXTwith aCHECKconstraint limiting it to predefined states ('received', 'unpaid', 'paid', 'shipped', 'delivered', 'cancelled'). -
created_at, which records when the order was placed, set asDATETIMEwith aDEFAULTof the current time.
created_at has a DEFAULT value, so there is no need to apply the NOT NULL constraint explicitly. Other columns have the NOT NULL constraint applied.
-
id, which specifies the unique ID for the order line item as anINTEGER. This column has thePRIMARY KEYconstraint applied. -
order_id, which is aFOREIGN KEYreferencing theorderstable. -
product_id, which is aFOREIGN KEYreferencing theproductstable. -
quantity, which represents the number of units purchased, set asINTEGER. -
unit_price_snapshot, which records the price of the product at the moment of purchase. It is set asDECIMAL(10, 2)to preserve historical data accuracy.
All columns have the NOT NULL constraint applied.
-
id, which specifies the unique ID for the transaction as anINTEGER. This column has thePRIMARY KEYconstraint applied. -
order_id, which is aFOREIGN KEYreferencing theorderstable. -
amount, which is the transaction value, set asDECIMAL(10, 2). -
status, which tracks the transaction state ('pending', 'completed', 'failed'), set asTEXTwith aCHECKconstraint. -
provider, which indicates the payment processor ('payu', 'p24', 'tpay', 'autopay', 'stripe', 'blik', 'transfer'), set asTEXTwith aCHECKconstraint.
All columns have the NOT NULL constraint applied.
The below entity relationship diagram describes the relationships among the entities in the e-commerce database.
As detailed by the diagram:
- An order can have only one address to be shipped but many orders can be made to one address. Many customers can order something to one address (e.g., in workplace).
- Warehouse can have only one address but address can't have any warehouse.
- Warehouses have plenty of products inside and products can be allocated in few warehouses at the same time.
-
Product can belong only to one category but category contains a lot of products.
-
One product can be in different quantity in different warehouses. One product can be (depending on quantity) in plenty of carts or orders as cart item or order item.
- This table serves as the bridge for the many-to-many relationship between products and warehouses.
-
Customers and addresses share a many-to-many relationship, a customer can save multiple locations (home, office), and one address can be associated with multiple users (e.g., family members or co-workers).
-
One customer can have plenty of carts and orders but one order (or cart) belongs to only one customer.
- One order can have plenty of payments (failed status) but payment can be referenced with only one order.
In database :
logistics:
- It eliminates the need for logistics staff to understand the complex underlying table relationships, providing exactly what is needed for shipping and inventory management.
customer_service
- It allows support staff to verify orders using names and emails, but it hides sensitive columns like password (hashed) and birth_date. This minimizes the risk of internal data leaks or accidental exposure of PII (Personally Identifiable Information).
search_customers: Quick searching customers. Specially valiuable in on-phone customer service.
search_orders: Allowing support staff to provide real-time order status updates to customers.
search_product_name: Optimizes the storefront search engine, ensuring customers receive immediate results when browsing the catalog.
search_inventory_product: Accelerates stock availability checks for both the website display and internal logistics management.
The current architecture does not include a system for Returns (RMA). There is no dedicated table to track returned items, reason for return, or the condition of the stock being sent back to the warehouse.
The design does not support Product Variants (e.g., phones with different storage) as a single entity. Each variant must be created as a completely separate product, which may lead to data redundancy.
