Skip to content

aadityamp01/Foodkart

Repository files navigation

🍱 Foodkart

Online food ordering service — a clean, extensible backend simulation built in C# with SOLID principles, OOP design patterns, and an in-memory data store.


📋 Table of Contents


Overview

Foodkart is a console-based food ordering platform where users can discover restaurants serviceable in their area, place orders, and rate their experience. Restaurant owners can register and manage their outlets. The system is built without any database — all data lives in-memory, making it easy to run and demo instantly.


Features

Feature Details
👤 User Registration & Login Unique phone number, auto-logout on new login
🏪 Restaurant Registration One specialised dish per restaurant, multiple pincodes
📦 Inventory Management Owners can add stock; orders deduct quantity
🗺️ Location Management Owners can update serviceable pincodes
🔍 Restaurant Discovery Filter by user's pincode; sort by price or rating
🛒 Order Placement Validates serviceability and available quantity
⭐ Reviews & Ratings 1–5 stars, optional comment; average rating computed live
📜 Order History (Bonus) Full order history per user

Architecture

The solution follows a layered architecture with clear separation of concerns:

┌─────────────────────────────────────────────┐
│               Program.cs (Driver)           │
└───────────────────┬─────────────────────────┘
                    │
┌───────────────────▼─────────────────────────┐
│            FoodkartFacade                   │  ← Single entry point
│        (Facade Pattern + Error Handling)    │
└──────┬───────────┬──────────────┬───────────┘
       │           │              │
┌──────▼──┐  ┌─────▼────┐  ┌─────▼──────┐
│  User   │  │Restaurant│  │   Order    │  ← Services
│ Service │  │ Service  │  │  Service   │
└──────┬──┘  └─────┬────┘  └─────┬──────┘
       │           │              │
┌──────▼───────────▼──────────────▼──────────┐
│         Repository Interfaces              │  ← Abstractions
│   IUserRepo  IRestaurantRepo  IOrderRepo   │
└──────┬───────────┬──────────────┬──────────┘
       │           │              │
┌──────▼───────────▼──────────────▼──────────┐
│          InMemory Repositories             │  ← Data layer
└─────────────────────────────────────────────┘

Project Structure

Foodkart/
│
├── Models/
│   ├── User.cs
│   ├── FoodItem.cs
│   ├── Restaurant.cs
│   ├── Review.cs
│   └── Order.cs
│
├── Interfaces/
│   ├── IRepositories.cs         # IUserRepository, IRestaurantRepository, IOrderRepository
│   ├── IServices.cs             # IUserService, IRestaurantService, IOrderService, IReviewService
│   └── IRestaurantSortStrategy.cs
│
├── Repositories/
│   └── InMemoryRepositories.cs  # In-memory implementations of all repos
│
├── Services/
│   ├── UserService.cs
│   ├── RestaurantService.cs
│   ├── OrderService.cs
│   └── ReviewService.cs
│
├── Strategies/
│   └── SortStrategies.cs        # SortByRatingDescending, SortByPriceAscending
│
├── Session/
│   └── UserSession.cs           # Manages currently logged-in user context
│
├── Facade/
│   └── FoodkartFacade.cs        # Unified API surface with centralised error handling
│
├── Bootstrap/
│   └── AppBootstrapper.cs       # Composition root — wires all dependencies
│
├── Program.cs                   # Driver / demo with all test cases
└── Foodkart.csproj

Design Principles

SOLID

Principle Implementation
Single Responsibility Each service class handles exactly one domain (users, restaurants, orders, reviews). UserSession only manages login state.
Open/Closed New sort strategies (e.g. sort by name) implement IRestaurantSortStrategyzero changes to existing code.
Liskov Substitution All interface implementations are fully interchangeable; swapping InMemoryRestaurantRepository for a DB-backed one requires no other changes.
Interface Segregation Four focused service interfaces (IUserService, IRestaurantService, IOrderService, IReviewService) — no fat interfaces.
Dependency Inversion All services depend on abstractions. Concrete types are only instantiated in AppBootstrapper.

Design Patterns

Pattern Usage
Facade FoodkartFacade is the sole entry point for the driver; centralises error handling so no try/catch leaks into Program.cs.
Strategy IRestaurantSortStrategy — pluggable sorting algorithms. Adding a new sort is a new class, nothing more.
Repository Data access is abstracted behind interfaces; swap to SQL/NoSQL with zero service-layer changes.
Composition Root AppBootstrapper.Build() is the only place new is used for wiring — clean manual DI.

Getting Started

Prerequisites

Run

git clone https://github.com/your-username/foodkart.git
cd foodkart/Foodkart
dotnet run

That's it — no database, no config, no environment variables.


Usage / API Reference

All operations are available through FoodkartFacade. Below are the public methods:

User

// Register a new user
app.RegisterUser(name, gender, phoneNumber, pincode);

// Login (auto-logs out the previous user)
app.LoginUser(phoneNumber);

Restaurant

// Register a restaurant (logged-in user becomes the owner)
// pincodes uses '/' as delimiter: "BTM/HSR"
app.RegisterRestaurant(name, pincodes, foodItemName, price, initialQty);

// Add inventory
app.UpdateQuantity(restaurantName, quantityToAdd);

// Update serviceable pincodes
app.UpdateLocation(restaurantName, pincodes);

// List serviceable restaurants for the logged-in user's pincode
// sortBy: "price" | "rating"
app.ShowRestaurants(sortBy);

Orders

// Place an order (validates pincode + stock)
app.PlaceOrder(restaurantName, quantity);

// View order history for any phone number
app.ShowOrderHistory(phoneNumber);

Reviews

// Rate a restaurant (rating: 1–5, comment optional)
app.RateRestaurant(restaurantName, rating);
app.RateRestaurant(restaurantName, rating, "Great food!");

Sample Demo Output

══════════════════════════════════════════
  SECTION 1: Register Users
══════════════════════════════════════════
[User] Registered: Pralove (phoneNumber-1) - HSR
[User] Registered: Nitesh (phoneNumber-2) - BTM
[User] Registered: Vatsal (phoneNumber-3) - BTM
[ERROR] User with phone 'phoneNumber-1' already exists.

══════════════════════════════════════════
  SECTION 3: show_restaurant (Vatsal, BTM)
══════════════════════════════════════════
[Show] Restaurants sorted by price:
  Food Court-1, NI Thali @ ₹100 | Rating: 0
  Food Court-2, Burger @ ₹120 | Rating: 0

══════════════════════════════════════════
  SECTION 4: Place Orders
══════════════════════════════════════════
[Order] Order Placed Successfully. Order#1 | Food Court-1 | NI Thali x2 | ₹200 | Placed
[Order] Cannot place order — insufficient quantity. Available: 3, Requested: 7.

══════════════════════════════════════════
  SECTION 6: show_restaurant by rating (Vatsal, BTM)
══════════════════════════════════════════
[Show] Restaurants sorted by rating:
  Food Court-1, NI Thali @ ₹100 | Rating: 5
  Food Court-2, Burger @ ₹120 | Rating: 3

══════════════════════════════════════════
  SECTION 7: update_quantity
══════════════════════════════════════════
[Restaurant] Updated: Food Court-2, BTM, Burger - 8
[ERROR] You do not own restaurant 'Food Court-3'.

══════════════════════════════════════════
  SECTION 8: update_location
══════════════════════════════════════════
[Restaurant] Updated: Food Court-2, "BTM/HSR", Burger - 8

Edge Cases Handled

  • ✅ Duplicate phone number on registration → clear error
  • ✅ Duplicate restaurant name → clear error
  • ✅ Order from restaurant not in user's pincode → rejected with message
  • ✅ Order quantity exceeds available stock → rejected with message
  • ✅ Invalid rating (outside 1–5) → exception thrown and caught
  • ✅ Non-owner trying to update restaurant → UnauthorizedAccessException
  • ✅ Operations without login → InvalidOperationException caught gracefully
  • ✅ Login auto-logs out previous user
  • ✅ Zero/negative quantity or price → ArgumentException

Extending the System

Add a new sort order (e.g. alphabetical)

// 1. Create the strategy — that's all
public class SortByNameAscending : IRestaurantSortStrategy
{
    public string SortKey => "name";
    public IOrderedEnumerable<Restaurant> Sort(IEnumerable<Restaurant> restaurants)
        => restaurants.OrderBy(r => r.Name);
}

// 2. Register it in AppBootstrapper
var sortStrategies = new List<IRestaurantSortStrategy>
{
    new SortByRatingDescending(),
    new SortByPriceAscending(),
    new SortByNameAscending()   // ← done
};

Swap to a real database

Implement IRestaurantRepository (or any repository interface) against your database of choice and update AppBootstrapper.Build(). No service code changes required.


License

MIT

About

Online food ordering service — a clean, extensible backend simulation built in C# with SOLID principles, OOP design patterns, and an in-memory data store.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages