Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

5 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎯 Low Level Design Learning Repository

A comprehensive learning repository for mastering Object-Oriented Programming (OOP) concepts, Class Relationships, and Design Principles through hands-on TypeScript implementations.


πŸ“š Table of Contents


πŸŽ“ Overview

This repository serves as a structured learning path for Low Level Design (LLD) concepts. Each module contains practical implementations, real-world examples, and comprehensive documentation to help you understand and apply OOP principles effectively.

Learning Path

OOP Fundamentals β†’ Class Relationships β†’ Design Principles β†’ Design Patterns
     βœ…                βœ…                    πŸ”„              πŸ“…

πŸ› οΈ Prerequisites

  • Node.js (v16 or higher)
  • TypeScript (v5.x)
  • Basic understanding of JavaScript/TypeScript
  • Familiarity with Object-Oriented Programming concepts

πŸš€ Getting Started

Installation

# Clone the repository
git clone <repository-url>
cd "Low Level Design"

# Install dependencies
npm install

# Run the project
npm run dev

Available Scripts

npm run dev    # Start development server with hot reload
npm run build  # Compile TypeScript to JavaScript
npm start      # Run the compiled JavaScript

βœ… Completed Modules

1. OOP Fundamentals

πŸ“¦ Encapsulation

Location: src/encapsulation/

Learn how to properly encapsulate data and behavior within classes.

  • Bad Example: BadBankAccount.ts - Demonstrates problems with public fields
  • Good Example: GoodBankAccount.ts - Shows proper encapsulation with private fields and controlled access

Key Concepts:

  • Private fields and methods
  • Public getters/setters
  • Data validation
  • Business rule enforcement

Example:

// ❌ Bad: Public field allows invalid states
class BadBankAccount {
  balance: number = 0; // Anyone can set this to -1000!
}

// βœ… Good: Encapsulated with validation
class GoodBankAccount {
  private balance: number = 0;

  public deposit(amount: number): void {
    if (amount <= 0) throw new Error("Amount must be positive");
    this.balance += amount;
  }
}

🎭 Abstraction

Location: src/Abstraction/

Understand how to hide implementation details and expose only what's necessary.

  • Notification System: NotificationSystem.ts - Interface defining notification contract
  • Implementations: EmailNotifier.ts, SMSNotifier.ts - Concrete implementations
  • Logger: Logger.ts - Abstraction example for logging

Key Concepts:

  • Interface-based design
  • Implementation hiding
  • Contract definition
  • Polymorphism through interfaces

Example:

// Abstraction: What can be done, not how
interface Notifier {
  send(message: string): void;
}

// Implementation: How it's done
class EmailNotifier implements Notifier {
  send(message: string): void {
    console.log(`Sending email: ${message}`);
  }
}

2. Class Relationships

This module covers the five fundamental relationships in OOP, demonstrated through practical examples.

πŸ”— Association

Location: src/Class Relationships/BuildingRiderSystem/entities/RideStrict.ts

Definition: A relationship where two classes are connected but can exist independently.

Real-world Example: In the Ride Booking System, a Ride is associated with a Driver and Rider. They know about each other, but can exist independently.

// Ride knows about Driver (association)
class Ride {
  private driverId?: string; // Associated, not owned

  public assignDriver(driverId: string): void {
    this.driverId = driverId;
  }
}

Key Points:

  • Objects can exist independently
  • Relationship is temporary or optional
  • No ownership implied

πŸ“¦ Aggregation

Location: src/Class Relationships/Aggregation.ts

Definition: A "has-a" relationship where the whole contains parts, but parts can exist without the whole.

Examples:

  • Order aggregates Item[] - Items can exist without an Order
  • Team aggregates Developer[] - Developers can exist without a Team
// Order aggregates Items (items can exist independently)
class Order {
  private items: Item[] = [];

  public addItem(item: Item): void {
    this.items.push(item); // Item exists independently
  }
}

// Team aggregates Developers
class Team {
  private developers: Developer[] = [];

  public addDeveloper(developer: Developer): void {
    this.developers.push(developer);
  }
}

Key Points:

  • "Has-a" relationship
  • Parts can exist independently
  • Whole doesn't create parts
  • Parts can be shared

πŸ—οΈ Composition

Location: src/Class Relationships/Composition.ts

Definition: A strong "owns-a" relationship where parts cannot exist without the whole.

Example: House composes Room[] - Rooms are created by and owned by the House.

// House composes Rooms (rooms cannot exist without house)
class House {
  private rooms: Room[] = [];

  constructor() {
    // Composition: House CREATES its rooms
    this.rooms = [
      new Room("Living Room"),
      new Room("Kitchen"),
      new Room("Bedroom"),
    ];
  }
}

Key Points:

  • "Owns-a" relationship
  • Parts are created by the whole
  • Parts cannot exist independently
  • Lifecycle is tied together

πŸ”„ Dependency

Location: src/Class Relationships/BuildingRiderSystem/services/

Definition: A relationship where one class uses another temporarily, often through method parameters.

Example: RideService depends on MatchingService to find drivers.

// RideService depends on MatchingService
class RideService {
  constructor(
    private matchingService: MatchingService // Dependency injection
  ) {}

  requestRide(input: RideInput): Ride {
    // Uses MatchingService temporarily
    const driver = this.matchingService.findNearestDriver(input.pickup);
    // ...
  }
}

Key Points:

  • Temporary usage relationship
  • Often through method parameters or constructor injection
  • No permanent ownership
  • Loose coupling

⚑ Realization

Location: src/Class Relationships/Realization.ts

Definition: A relationship where a class implements an interface, defining a contract.

Example: Multiple payment methods realize the Payment interface.

// Interface defines the contract
interface Payment {
  pay(amount: number): void;
}

// Classes realize the interface
class CreditCardPayment implements Payment {
  pay(amount: number): void {
    console.log(`Paying ${amount} with credit card`);
  }
}

class PayPalPayment implements Payment {
  pay(amount: number): void {
    console.log(`Paying ${amount} with PayPal`);
  }
}

Key Points:

  • "Implements" relationship
  • Contract definition through interfaces
  • Multiple classes can realize the same interface
  • Enables polymorphism

3. SOLID Principles

Location: src/SOLID/

βœ… S β€” Single Responsibility Principle (SRP)

Location: src/SOLID/SingleResPrinciple/

What we learned:

  • One reason to change: keep each class focused on a single responsibility (persistence vs calculation vs formatting vs delivery).
  • Better testing & change isolation: you can change email logic without touching salary calculation (and vice-versa).
  • Clean entry point: keep demos/orchestration in a single run...() function and call it from src/index.ts.

Files:

  • SRP.ts β€” Employee entity (data + getters)
  • EmpRepo.ts β€” EmployeeRepository (persistence)
  • PaySlipCalc.ts β€” net pay calculation
  • PayslipGen.ts β€” payslip formatting
  • EmailPayslip.ts β€” email sending
  • SRPDemo.ts β€” orchestration (runSRPDemo())

βœ… O β€” Open/Closed Principle (OCP)

Location: src/SOLID/OpenClosed/

What we learned:

  • Open for extension, closed for modification: PaymentProcessor depends on the PaymentMethod interface, so new payment types don’t require editing the processor.
  • Add new behavior by adding a class: to support UPI, we add UPIPayment implements PaymentMethod (no if/else, no switch in the processor).
  • Important TypeScript pitfall: a .ts file with no imports/exports is treated as a script, which can leak declarations into the global scope.
    • We hit this because src/Class Relationships/Realization.ts defines global CreditCardPayment/PayPalPayment with pay(), while OCP expects processPayment().
    • Fix: in OCP usage files, import concrete implementations from the correct module (./OnlinePayment) so they correctly satisfy PaymentMethod.

Files:

  • PaymentMethod.ts β€” interface/contract
  • PaymentProcessor.ts β€” uses PaymentMethod (stable code)
  • OnlinePayment.ts β€” concrete implementations (CreditCardPayment, PayPalPayment, UPIPayment)
  • CheckoutService.ts β€” demo usage
  • WithoutOpenClosed.ts β€” anti-pattern example (needs modification for new methods)

βœ… L β€” Liskov Substitution Principle (LSP)

Location: src/SOLID/LiskovSubstitution/

What we learned:

  • Subtypes must keep promises of supertypes: if code expects a Bird that can fly, every subtype used in that place must really behave like a flying bird.
  • Don’t lie with inheritance: avoid child classes that override methods in a way that breaks expectations (e.g. a Penguin that β€œflies” but actually can’t).
  • Model abilities explicitly: split capabilities into more precise types (e.g. Bird vs FlyingBird) so you only use each subtype where it is truly valid.

Files:

  • LSP.ts β€” correct design with Bird, FlyingBird, Sparrow, Penguin
  • LSPDemo.ts β€” story-style demo (runLSPDemo())
  • WithoutLSP.ts β€” anti-pattern example with a non-flying Penguin that still extends Bird

βœ… I β€” Interface Segregation Principle (ISP)

Location: src/SOLID/InterfaceSegregation/

What we learned:

  • Small, focused interfaces: give each client only the methods it really needs instead of one giant interface.
  • Avoid β€œfat” interfaces: big interfaces force classes to implement unused methods, leading to fake/empty implementations and confusion.
  • Design by role: split abilities into tiny interfaces (CanWalk, CanTalk, CanTakePhoto) and compose them per need (robot dog vs camera toy).

Files:

  • ISP.ts β€” good design with small capability interfaces and RobotDog / CameraToy
  • ISPDemo.ts β€” story-style demo (runISPDemo())
  • WithoutISP.ts β€” anti-pattern with a single SuperToy interface and a bloated implementation

πŸš— Comprehensive Example: Ride Booking System

Location: src/Class Relationships/BuildingRiderSystem/

A complete, production-like implementation demonstrating all class relationships in a real-world scenario.

Project Structure

BuildingRiderSystem/
β”œβ”€β”€ entities/              # Domain entities
β”‚   β”œβ”€β”€ Driver.ts         # Driver entity
β”‚   β”œβ”€β”€ Rider.ts          # Rider entity
β”‚   β”œβ”€β”€ RideStrict.ts     # Ride entity (shows Association)
β”‚   β”œβ”€β”€ RiderEvent.ts     # Event entity (shows Composition)
β”‚   └── PaymentReceipt.ts # Payment receipt
β”œβ”€β”€ services/             # Business logic services
β”‚   β”œβ”€β”€ RideService.ts    # Main ride orchestration
β”‚   β”œβ”€β”€ MatchingService.ts # Driver matching logic
β”‚   β”œβ”€β”€ PricingService.ts # Fare calculation
β”‚   β”œβ”€β”€ PaymentService.ts # Payment processing
β”‚   └── payments/         # Payment implementations (Realization)
β”‚       β”œβ”€β”€ CashPayment.ts
β”‚       β”œβ”€β”€ UPIPayment.ts
β”‚       └── CardPayment.ts
β”œβ”€β”€ repository/           # Data access layer
β”‚   β”œβ”€β”€ DriverRepository.ts
β”‚   β”œβ”€β”€ RiderRepository.ts
β”‚   └── RideRepository.ts
β”œβ”€β”€ interfaces/           # Contracts
β”‚   └── PaymentMethod.ts  # Payment interface (Realization)
β”œβ”€β”€ UML/                  # Design documentation
β”‚   β”œβ”€β”€ 01-UseCaseDiagram.md
β”‚   β”œβ”€β”€ 02-ClassDiagram.md
β”‚   └── 03-StateDiagram.md
└── ProjectContext.md     # Project requirements and design decisions

Relationships Demonstrated

Relationship Example File
Association Ride ↔ Driver entities/RideStrict.ts
Aggregation MatchingService has Driver[] services/MatchingService.ts
Composition Ride owns RideEvent[] entities/RideStrict.ts
Dependency RideService uses MatchingService services/RideService.ts
Realization UPIPayment implements PaymentMethod services/payments/

Features

  • βœ… Complete ride lifecycle (REQUESTED β†’ ACCEPTED β†’ IN_PROGRESS β†’ COMPLETED)
  • βœ… Driver matching algorithm
  • βœ… Fare calculation
  • βœ… Multiple payment methods (polymorphism)
  • βœ… State management with validation
  • βœ… Repository pattern
  • βœ… Service layer architecture
  • βœ… UML diagrams

Running the Example

// See src/index.ts for complete example
const ride = rideService.requestRide({
  rideId: "ride-101",
  riderId: rider.getId(),
  pickup: new Location(19.08, 72.88),
  drop: new Location(19.1, 72.9),
});

rideService.startRide(ride.getId());
rideService.completeRide(ride.getId());

const receipt = paymentService.payForRide({
  rideId: ride.getId(),
  durationMin: 18,
  method: new UPIPayment(), // πŸ”₯ Polymorphism in action
});

πŸ“ Project Structure

Low Level Design/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ Abstraction/              # Abstraction examples
β”‚   β”‚   β”œβ”€β”€ NotificationSystem.ts
β”‚   β”‚   β”œβ”€β”€ EmailNotifier.ts
β”‚   β”‚   β”œβ”€β”€ SMSNotifier.ts
β”‚   β”‚   └── Logger.ts
β”‚   β”‚
β”‚   β”œβ”€β”€ Class Relationships/      # Class relationship examples
β”‚   β”‚   β”œβ”€β”€ Aggregation.ts        # Aggregation examples
β”‚   β”‚   β”œβ”€β”€ Composition.ts       # Composition examples
β”‚   β”‚   β”œβ”€β”€ Realization.ts       # Realization examples
β”‚   β”‚   └── BuildingRiderSystem/ # Comprehensive example
β”‚   β”‚       β”œβ”€β”€ entities/
β”‚   β”‚       β”œβ”€β”€ services/
β”‚   β”‚       β”œβ”€β”€ repository/
β”‚   β”‚       β”œβ”€β”€ interfaces/
β”‚   β”‚       └── UML/
β”‚   β”‚
β”‚   β”œβ”€β”€ encapsulation/            # Encapsulation examples
β”‚   β”‚   └── BadBankAccount.ts
β”‚   β”‚
β”‚   └── index.ts                  # Main entry point
β”‚
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
└── README.md

πŸ”œ Next Steps

Design Principles (In Progress)

The next phase will cover SOLID principles and other fundamental design principles:

  • Single Responsibility Principle (SRP)
  • Open/Closed Principle (OCP)
  • Liskov Substitution Principle (LSP)
  • Interface Segregation Principle (ISP)
  • Dependency Inversion Principle (DIP)
  • DRY (Don't Repeat Yourself)
  • KISS (Keep It Simple, Stupid)
  • YAGNI (You Aren't Gonna Need It)

Design Patterns (Future)

  • Creational Patterns (Factory, Builder, Singleton)
  • Structural Patterns (Adapter, Decorator, Facade)
  • Behavioral Patterns (Observer, Strategy, Command)

🎯 Learning Objectives

By completing this repository, you will:

βœ… Understand core OOP concepts (Encapsulation, Abstraction)
βœ… Master all five class relationships (Association, Aggregation, Composition, Dependency, Realization)
βœ… Apply design principles in real-world scenarios
βœ… Build maintainable, scalable, and testable code
βœ… Understand when to use each relationship type
βœ… Design systems with proper separation of concerns


πŸ“ Notes

  • All examples are written in TypeScript for type safety and better OOP support
  • Code follows production-grade practices with proper error handling
  • Each module includes comments explaining the concepts
  • The Ride Booking System serves as a comprehensive example combining all concepts

🀝 Contributing

This is a personal learning repository. If you find any issues or have suggestions:

  1. Create an issue describing the problem or suggestion
  2. Fork the repository (if applicable)
  3. Make your changes
  4. Submit a pull request

πŸ“„ License

This project is for educational purposes.


πŸ™ Acknowledgments

  • Inspired by real-world system design challenges
  • Based on industry best practices and design patterns
  • Built to understand Low Level Design concepts deeply

Happy Learning! πŸš€

About

Understanding LLD by doing some small hands on exercises

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages