Low-Level Design (LLD) of ClearFood in JavaScript
Introduction
ClearFood is a food ordering system where users can order from registered restaurants that serve specific PIN codes. Users can rate restaurants and view serviceable restaurants based on ratings or prices.
Key Features:
- Users can register and place orders.
- Restaurants can register and update food quantity.
- Users can rate restaurants with or without comments.
- Restaurants are filtered by price or rating.
- Users can fetch their order history.
Class Design
Restaurant Class
class Restaurant {
constructor(name, pincodes, foodName, foodPrice, initialQuantity) {
this.name = name;
this.pincodes = new Set(pincodes);
this.foodName = foodName;
this.foodPrice = foodPrice;
this.quantity = initialQuantity;
this.totalRating = 0;
this.numberOfRatings = 0;
}
updateQuantity(quantityToAdd) {
this.quantity += quantityToAdd;
console.log(`Updated quantity for ${this.name}: ${this.quantity}`);
}
addRating(rating) {
this.totalRating += rating;
this.numberOfRatings++;
}
getAverageRating() {
return this.numberOfRatings === 0 ? 0 : this.totalRating / this.numberOfRatings;
}
}
User Class
class User {
constructor(name, phoneNumber, pincode) {
this.name = name;
this.phoneNumber = phoneNumber;
this.pincode = pincode;
this.orders = [];
}
placeOrder(restaurant, quantity) {
if (!restaurant.pincodes.has(this.pincode)) {
console.log("Restaurant does not deliver to your location.");
return;
}
if (restaurant.quantity < quantity) {
console.log("Cannot place order. Insufficient quantity.");
return;
}
restaurant.quantity -= quantity;
this.orders.push({ restaurant: restaurant.name, quantity });
console.log(`Order placed for ${quantity} ${restaurant.foodName} from ${restaurant.name}`);
}
getOrderHistory() {
return this.orders;
}
rateRestaurant(restaurant, rating, comment = "") {
restaurant.addRating(rating);
console.log(`Rated ${restaurant.name}: ${rating} stars. Comment: ${comment}`);
}
}
Helper Functions
class FoodOrderingSystem {
constructor() {
this.restaurants = [];
this.users = [];
}
registerRestaurant(name, pincodes, foodName, foodPrice, initialQuantity) {
let restaurant = new Restaurant(name, pincodes, foodName, foodPrice, initialQuantity);
this.restaurants.push(restaurant);
console.log(`Restaurant ${name} registered successfully.`);
}
registerUser(name, phoneNumber, pincode) {
let user = new User(name, phoneNumber, pincode);
this.users.push(user);
console.log(`User ${name} registered successfully.`);
}
showRestaurants(sortBy) {
let serviceableRestaurants = this.restaurants.filter(r => r.quantity > 0);
if (sortBy === "price") {
serviceableRestaurants.sort((a, b) => a.foodPrice - b.foodPrice);
} else if (sortBy === "rating") {
serviceableRestaurants.sort((a, b) => b.getAverageRating() - a.getAverageRating());
}
return serviceableRestaurants.map(r => `${r.name}, ${r.foodName} - ${r.foodPrice}`);
}
}
Simulating the System
const system = new FoodOrderingSystem();
// Register Users
system.registerUser("Pralove", "1234567890", "HSR");
system.registerUser("Nitesh", "0987654321", "BTM");
// Register Restaurants
system.registerRestaurant("Food Court-1", ["BTM", "HSR"], "NI Thali", 100, 5);
system.registerRestaurant("Food Court-2", ["BTM"], "Burger", 120, 3);
// Fetch Restaurants by Price
console.log("Restaurants sorted by price:", system.showRestaurants("price"));
// Place Orders
let user = system.users[0];
let restaurant = system.restaurants[0];
user.placeOrder(restaurant, 2);
// Rate Restaurant
user.rateRestaurant(restaurant, 5, "Amazing food!");
// Fetch Restaurants by Rating
console.log("Restaurants sorted by rating:", system.showRestaurants("rating"));
Conclusion
This implementation follows OOP principles, ensuring encapsulation and modularity. Future improvements can include:
- Support for multiple food items per restaurant.
- Database integration instead of in-memory storage.
- Optimized sorting for large datasets.
This LLD ensures a scalable and maintainable system. 🚀