Wayfair | Bike Rental Shop
Anonymous User
544
  1. Bike Rental Shop

Problem Explanation:-

Products -> Bikes, Scooters

Bike -> 3 sizes (Small, Medium, Large)
Scooter -> 2 styles (Electric, gas)

Oprations:

  1. Inventory Management
  2. Rental Management (Who rented what, when, overdue etc)
  3. Customer Management (Balances, rentals, charges)

Queries required:

  1. How many small bikes exits?
  2. Available products for rent?
  3. Customer balance?
  4. Product currently rented?
  5. Overdue rentals (who has them)?
  6. Customer's rented products?

Commands requied:

  1. add/remove product in inventory
  2. add customer
  3. record rental
  4. create charges

Deliverables said:

  1. API spec

  2. Class diagram

  3. Schema diagram

  4. Sequence diagrams(optionl)

  5. Design Approach

We will follow Object-Oriented Design + Database Schema + REST APIs.

Why this approach?

Separation of Concerns → Inventory, Customer, Rentals, Payments are different modules.

Extensibility → Easy to add new product types (e.g., e-bikes, motorcycles).

Maintainability → Clear entity relationships make future queries simple.

Reusability → Interfaces and abstract classes allow plugging in different rental products.

3 — Java classes & interfaces (essential parts)

public enum ProductType{
BIKE, SCOOTER
}

public abstract class Product{
private Long productId;
private ProductType type; //
private boolean available;
private double basePricePerDay;

//getter and setter

public abstract String productDetails();

}

public enum Size{
SMALL,
MEDIUM,
LARGE
}

public class Bike extends Product{
private Size size;

//getter and setter

}

public enum Style{
ELECTRIC,
GAS
}

public class Scooter extends Product{
private Style style;

//getter and setter

}

public class Customer{

private long id;
private String name;
private String email;
private double balance;

//getter and setter

}

public class RentalStatus{
ACTIVE, RETURNED, LOST, CANCELLED
}

public class Rental{

private Long id;
private Long productId;
private Long customerId;
private LocalDateTime startAt;
private LocalDateTime dueAt;
private LocalDateTime returnedAt;
private double priceCharged;
private RentalStatus status;

// getter and setter

}

public class Charge{
private Long id;
private Long customerId;
private double amount;
private String reason;
private LocalDateTime createdAt;
}

Service interfaces [InventoryService, CustomerService, RentalService]

public interface InventoryService{

Product addProduct(Product product);
void removeProduct(Long productId); // permanent remove logical and physical
Optional<Product> getProduct(Long productId);
List<Product> listAvailableProducts(ProductFilter filter);
long countBikeBySize(Bike.Size size);

}

public interface CustomerService{

Customer createCustomer(CustomerCreateDTO dto);
Customer getCustomer(Long id);
double getBalance(Long customerId);
List<Rental> getCustomerRental(Long customerId);

}

public interface RentalService{

Rental rentProduct(Long customerId, Long productId, LocalDateTime dueAt) throw NotAvailableException;
Rental returnProduct(Long rentalId, LocalDateTime returnedAt);
List<Rental> listOverdueRentals();

}

  1. Database Schema

Tables:

Customer

customer_id (PK)

name

balance

Product

product_id (PK)

type (bike or scooter)

is_available

Bike

bike_id (FK → Product)

size (SMALL, MEDIUM, LARGE)

Scooter

scooter_id (FK → Product)

style (ELECTRIC, GAS)

Rental

rental_id (PK)

product_id (FK → Product)

customer_id (FK → Customer)

start_date

due_date

return_date

charges

✅ Reasoning:

Product table for shared properties.

Bike and Scooter as subtype tables (Single Table Inheritance possible, but we want flexibility).

Rental table links customer ↔ product.

CREATE TABLE product (
id BIGSERIAL PRIMARY KEY,
sku VARCHAR(64) UNIQUE NOT NULL,
type VARCHAR(20) NOT NULL, -- BIKE, SCOOTER, ...
available BOOLEAN DEFAULT TRUE,
base_price_per_day NUMERIC(10,2) DEFAULT 0.0,
created_at TIMESTAMP DEFAULT now(),
deleted_at TIMESTAMP NULL
);

CREATE TABLE bike (
product_id BIGINT PRIMARY KEY REFERENCES product(id),
size VARCHAR(10) NOT NULL -- SMALL, MEDIUM, LARGE
);

CREATE TABLE scooter (
product_id BIGINT PRIMARY KEY REFERENCES product(id),
style VARCHAR(10) NOT NULL -- ELECTRIC, GAS
);

CREATE TABLE customer (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
email VARCHAR(255) UNIQUE,
balance NUMERIC(12,2) DEFAULT 0.0,
created_at TIMESTAMP DEFAULT now()
);

CREATE TABLE rental (
id BIGSERIAL PRIMARY KEY,
product_id BIGINT NOT NULL REFERENCES product(id),
customer_id BIGINT NOT NULL REFERENCES customer(id),
start_at TIMESTAMP NOT NULL DEFAULT now(),
due_at TIMESTAMP NOT NULL,
returned_at TIMESTAMP NULL,
price_charged NUMERIC(10,2) DEFAULT 0.0,
status VARCHAR(20) NOT NULL, -- ACTIVE, RETURNED...
created_at TIMESTAMP DEFAULT now()
);

CREATE TABLE charge (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL REFERENCES customer(id),
amount NUMERIC(12,2) NOT NULL,
reason VARCHAR(255),
created_at TIMESTAMP DEFAULT now()
);

-- Indexes for queries
CREATE INDEX idx_product_type_available ON product(type, available);
CREATE INDEX idx_bike_size ON bike(size);
CREATE INDEX idx_rental_status_due ON rental(status, due_at);
CREATE INDEX idx_customer_email ON customer(email);

Why normalized?

Avoids storing bike-specific columns in product (redundant nulls).

Makes constraints (bike.size allowed values) explicit.

Easier to add new product types (add table + map to product row).

  1. API Specification

Inventory APIs

POST /inventory/product → Add a product

DELETE /inventory/product/{id} → Remove product

GET /inventory/products?type=bike&available=true → List available products

GET /inventory/products/bikes/count?size=SMALL → Query small bikes

Customer APIs

POST /customers → Add customer

GET /customers/{id} → customer details

GET /customers/{id}/balance → Check balance

GET /customers/{id}/rentals → Get customer rentals

Rental APIs

POST /rentals → Rent a product (customerId, productId, dueDate)

POST /rentals/{id}/return → Return a product

GET /rentals/overdue → Find overdue rentals

Charges

POST customers/{id}/charges → create a charge (e.g., late fee)

GET customers/{id}/ledger → list charges

✅ Reasoning:

REST fits naturally with resources (Customer, Product, Rental).

Queries map well to GET requests.

Commands map well to POST/DELETE.

  1. How This Solves Queries

How many small bikes?
→ SELECT COUNT(*) FROM Bike WHERE size='SMALL' AND product_id IN (SELECT product_id FROM Product WHERE is_available=true);

What products for rent?
→ GET /inventory/products

Does customer owe balance?
→ GET /customers/{id}/balance

What products are rented?
→ GET /rentals

Overdue rentals?
→ GET /rentals/overdue

What products has a customer rented?
→ GET /customers/{id}/rentals

===============

ToDos :-
Entities Class must
Database Schemas
API designs

enum ProductType{
BIKE,
SCOOTER
}

enum BikeSize{
SMALL,
MEDIUM,
LARGE
}

enum ScootorStyle{
ELECTIRC,
GAS
}

abstract class Product{
private String productId;
private ProductType type;
privateboolean isAvailable;
// private double perDayCharge;

//getter setter
}

class Bike extends Product{
BikeSize size;

// getter setter

}

class Scooter extends Product{
ScootorStyle style;

// getter setter

}

class Customer{
String customerId;
String name;
String phoneNumber;
String balance;

// getter setter

}

class Rental{
String rentalId;
String customerId;
String productId;
LocalDateTime startDate;
LocalDateTime endDate;
LocalDateTime returnedDate;
double charge;

// getter setter

}

Database Schemas:-

Customer

  • customerId [PK]
  • name
  • phoneNumber
  • balance

Product

  • productId [PK]
  • type ENUM {BIKE, SCOOTER}
  • isAvailable

Bike

  • productId [FK]
  • size

Scooter

  • productId [FK]
  • style

// Update

  • productId
  • sizeOrStyle

Rental

  • rentalId [PK]
  • customerId [FK]
  • productId [FK]
  • startDate
  • endDate
  • returnedDate
  • chargeId

Charge

  • chargeId
  • customerId
  • advancePaid
  • totalAmount

inteface Inventory{
Product addProduct(Product product);
void removeProduct(String productId);
List getAllAvaialbleProducts();
int getBikeBySize(BikeSize size);
}

inteface Customer{
Customer addCustomer(Customer customer);
double getCustomerBalance(String customerId);
List getCustomerRentedProducts(String customerId);
}

inteface Rental{
Rental rentProduct(String customerId, String productId, LocalDateTime endDate);
void returnProduct(String productId, LocalDateTime currtentDate);
}

Comments (2)