DataBase Design Lesson 35 – Mini Project | Dataplexa
Database Design · Lesson 35

Mini Project

Build a complete database system for BookVault from scratch, applying all design principles learned throughout this module.

Project Overview: BookVault Database System

Your mission: design and implement the complete database for BookVault, our online bookstore. This project combines every concept from the previous 34 lessons. You'll identify entities, create ER diagrams, normalize tables, implement physical design, optimize for performance, and consider scalability.

Think Amazon's book section, but simpler. Customers browse books, place orders, leave reviews. Authors publish books through publishers. Books belong to categories. Inventory gets managed. Sales get tracked.

Project Scope

You'll handle customer management, book catalog, order processing, inventory tracking, author management, reviews system, and basic analytics. Real-world complexity, but focused scope.

Phase 1: Requirements Analysis

Start with business requirements. What does BookVault actually need to do?

Customer Operations

Registration, login, profile management, order history, wishlist, reviews

Book Management

Catalog browsing, search, categories, author info, inventory, pricing

Order Processing

Shopping cart, checkout, payment tracking, shipping, order status

Business Analytics

Sales reports, popular books, customer behavior, inventory alerts

Key Business Rules

Every database has rules. BookVault's rules drive our design decisions.

  • One customer can place multiple orders
  • One order contains multiple books (different quantities)
  • One book can have multiple authors (think textbooks)
  • One author writes multiple books
  • Books belong to one primary category, but can have subcategories
  • Customers can review books they've purchased
  • Inventory must be tracked and updated with each sale

Phase 2: Entity-Relationship Design

Now we identify entities and relationships. Think nouns (entities) and verbs (relationships).

CUSTOMERS
Attributes
PK customer_id
first_name
last_name
email
password_hash
city
created_at
BOOKS
Attributes
PK book_id
FK category_id
title
isbn
price
stock_qty
published_year
description
AUTHORS
Attributes
PK author_id
first_name
last_name
nationality
birth_year
biography
ORDERS
Attributes
PK order_id
FK customer_id
order_date
status
total_amount
shipping_address
ORDER_ITEMS
Attributes
PK order_item_id
FK order_id
FK book_id
quantity
unit_price
CATEGORIES
Attributes
PK category_id
name
FK parent_category_id
description

Relationship Analysis

The trickiest part? Many-to-many relationships need junction tables. Books and authors have a many-to-many relationship - one book can have multiple authors, one author writes multiple books.

-- Junction table for Books-Authors many-to-many relationship
-- Eliminates redundancy and maintains data integrity
CREATE TABLE book_authors (
    book_id INT,
    author_id INT,
    author_order SMALLINT DEFAULT 1,
    PRIMARY KEY (book_id, author_id),
    FOREIGN KEY (book_id) REFERENCES books(book_id),
    FOREIGN KEY (author_id) REFERENCES authors(author_id)
);

Phase 3: Normalization Strategy

Time for normalization. We'll apply First, Second, and Third Normal Form systematically.

Before — Unnormalized
order_id customer_name books
1001 John Doe Python Guide, SQL Basics
1002 Jane Smith Python Guide

Problems: Repeating groups, update anomalies

After — Normalized
order_id customer_id book_id
1001 501 201
1001 501 202

Clean: Atomic values, proper relationships

Phase 4: Physical Implementation

Now we create the actual tables. This is where theory meets reality.

-- Create BookVault database schema
-- Start with customers - the foundation of our business
CREATE TABLE customers (
    customer_id INT PRIMARY KEY AUTO_INCREMENT,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    city VARCHAR(50),
    phone VARCHAR(20),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Table created successfully.
-- Categories table with self-referencing hierarchy
-- Supports parent/child category structure (Fiction -> Mystery)
CREATE TABLE categories (
    category_id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    parent_category_id INT,
    description TEXT,
    FOREIGN KEY (parent_category_id) REFERENCES categories(category_id)
);
Table created successfully.
-- Books table - the heart of our inventory system
-- Includes business-critical fields like stock and pricing
CREATE TABLE books (
    book_id INT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    isbn VARCHAR(13) UNIQUE,
    category_id INT NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    stock_qty INT DEFAULT 0,
    published_year YEAR,
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (category_id) REFERENCES categories(category_id),
    INDEX idx_title (title),
    INDEX idx_category (category_id),
    INDEX idx_stock (stock_qty)
);
Table created successfully.

What just happened?

We created indexes on title, category_id, and stock_qty because customers will search by title, browse by category, and we need to quickly check stock levels.

Phase 5: Performance Optimization

Real databases need real performance. Indexes make queries fast, but slow down inserts. We need balance.

-- Create composite index for common query patterns
-- Customers often search: "Show me books in Fiction under $20"
CREATE INDEX idx_category_price ON books(category_id, price);

-- Orders are frequently queried by customer and date
CREATE INDEX idx_customer_date ON orders(customer_id, order_date);

-- Email lookups need to be lightning fast for login
CREATE UNIQUE INDEX idx_customer_email ON customers(email);
Indexes created successfully.

Query Performance Testing

-- Test query: Find all mystery books under $25
-- This query hits our composite index perfectly
EXPLAIN SELECT b.title, b.price, c.name as category
FROM books b
JOIN categories c ON b.category_id = c.category_id
WHERE c.name = 'Mystery' AND b.price < 25.00
ORDER BY b.price;
+----+-------+--------+------+---------------+---------+
| id | type  | table  | key  | possible_keys | ref     |
+----+-------+--------+------+---------------+---------+
| 1  | ref   | books  | idx  | category_price| const   |
| 2  | eq_ref| cat    | pri  | PRIMARY       | b.cat_id|
+----+-------+--------+------+---------------+---------+

Data Insight

The EXPLAIN output shows our index is being used (key = idx). Without this index, MySQL would scan all 50,000 books. With it, we scan maybe 200 mystery books.

Phase 6: Advanced Features

Modern databases need modern features. Reviews, ratings, search functionality, and analytics.

-- Reviews table with rating and helpful votes
-- Supports Amazon-style review system
CREATE TABLE reviews (
    review_id INT PRIMARY KEY AUTO_INCREMENT,
    customer_id INT NOT NULL,
    book_id INT NOT NULL,
    rating TINYINT CHECK (rating BETWEEN 1 AND 5),
    review_text TEXT,
    helpful_votes INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id),
    FOREIGN KEY (book_id) REFERENCES books(book_id),
    UNIQUE KEY unique_customer_book (customer_id, book_id)
);
Table created successfully.
-- Analytics view for business intelligence
-- Calculates average ratings and review counts
CREATE VIEW book_analytics AS
SELECT 
    b.book_id,
    b.title,
    b.price,
    b.stock_qty,
    COALESCE(AVG(r.rating), 0) as avg_rating,
    COUNT(r.review_id) as review_count,
    SUM(oi.quantity) as total_sold
FROM books b
LEFT JOIN reviews r ON b.book_id = r.book_id
LEFT JOIN order_items oi ON b.book_id = oi.book_id
GROUP BY b.book_id, b.title, b.price, b.stock_qty;
View created successfully.

Testing the Complete System

-- Insert sample data to test relationships
-- Real data reveals design flaws quickly
INSERT INTO customers (first_name, last_name, email, city) VALUES
('John', 'Doe', 'john@example.com', 'New York'),
('Jane', 'Smith', 'jane@example.com', 'Chicago');

INSERT INTO categories (name, description) VALUES
('Fiction', 'Fictional literature'),
('Mystery', 'Mystery and thriller books'),
('Programming', 'Computer programming guides');

INSERT INTO authors (first_name, last_name, nationality) VALUES
('Agatha', 'Christie', 'British'),
('Robert', 'Martin', 'American');
3 rows inserted into customers.
3 rows inserted into categories.
2 rows inserted into authors.

Common Mistake

Don't forget to populate junction tables like book_authors. Many-to-many relationships break without the connecting data.

Project Validation

How do you know if your database design works? Test it with real scenarios.

1
Customer Registration & Login
2
Browse Books by Category
3
Add Books to Cart & Checkout
4
Leave Reviews & View Analytics

Your database passes if every user story works smoothly. Can customers find books? Do orders process correctly? Are reviews tied to the right books? If any step fails, revisit your relationships.

Success Criteria: Your BookVault database handles 1000+ customers, 5000+ books, complex searches under 100ms, maintains data integrity during concurrent orders, and provides accurate business analytics.

Quiz

1. In the BookVault database, how should you handle the many-to-many relationship between books and authors?


2. Why would you create a composite index on (category_id, price) in the BookVault books table?


3. In the BookVault reviews system, how do you prevent customers from reviewing the same book multiple times?


Up Next

Course Complete!

You've mastered database design from theory to practice - time to build your own systems with confidence.