LLD Problem

Design Library Management

Design a library management system to manage books, members, borrowing, returns, and late fees.

20 min readClassic LLD Problem

1Requirements

Functional Requirements
  • Add/remove/update books
  • Search books by title, author, ISBN
  • Register members
  • Borrow and return books
  • Reserve books
  • Calculate and collect fines
  • Send notifications for due dates
Questions to Ask
  • How many copies of each book?
  • Borrowing limit per member?
  • Loan duration?
  • Fine calculation method?
  • Reservation queue mechanism?
  • Different member types (student, faculty)?

2Class Design

Singleton
Library
- name
- books
- members
+ addBook()
+ registerMember()
+ searchBook()
Class
Book
- isbn
- title
- author
- copies[]
+ getAvailableCopies()
Class
BookCopy
- id
- book
- status
+ checkout()
+ return()
Abstract
Member
- id
- name
- loans[]
+ borrow()
+ return()
+ getMaxBooks()
Class
Loan
- bookCopy
- member
- dueDate
- returnDate
+ isOverdue()
+ calculateFine()
Class
Reservation
- book
- member
- timestamp
+ notify()

3Implementation

Library Management System
// ========== Enums ==========
enum BookStatus { AVAILABLE, BORROWED, RESERVED, LOST }
enum MemberType { STUDENT, FACULTY, REGULAR }

// ========== Book ==========
class Book {
    private String isbn;
    private String title;
    private String author;
    private List<BookCopy> copies;
    
    public Book(String isbn, String title, String author) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.copies = new ArrayList<>();
    }
    
    public void addCopy() {
        copies.add(new BookCopy(this, copies.size() + 1));
    }
    
    public BookCopy getAvailableCopy() {
        return copies.stream()
            .filter(c -> c.getStatus() == BookStatus.AVAILABLE)
            .findFirst()
            .orElse(null);
    }
    
    public String getTitle() { return title; }
    public String getIsbn() { return isbn; }
}

class BookCopy {
    private int id;
    private Book book;
    private BookStatus status;
    
    public BookCopy(Book book, int id) {
        this.book = book;
        this.id = id;
        this.status = BookStatus.AVAILABLE;
    }
    
    public void setStatus(BookStatus s) { this.status = s; }
    public BookStatus getStatus() { return status; }
    public Book getBook() { return book; }
}

// ========== Member ==========
abstract class Member {
    protected String id;
    protected String name;
    protected List<Loan> activeLoans;
    
    public Member(String id, String name) {
        this.id = id;
        this.name = name;
        this.activeLoans = new ArrayList<>();
    }
    
    public abstract int getMaxBooks();
    public abstract int getLoanDays();
    
    public boolean canBorrow() {
        return activeLoans.size() < getMaxBooks();
    }
    
    public void addLoan(Loan loan) {
        activeLoans.add(loan);
    }
    
    public void removeLoan(Loan loan) {
        activeLoans.remove(loan);
    }
}

class StudentMember extends Member {
    public StudentMember(String id, String name) { super(id, name); }
    public int getMaxBooks() { return 5; }
    public int getLoanDays() { return 14; }
}

class FacultyMember extends Member {
    public FacultyMember(String id, String name) { super(id, name); }
    public int getMaxBooks() { return 10; }
    public int getLoanDays() { return 30; }
}

// ========== Loan ==========
class Loan {
    private BookCopy bookCopy;
    private Member member;
    private LocalDate issueDate;
    private LocalDate dueDate;
    private LocalDate returnDate;
    private static final double FINE_PER_DAY = 0.50;
    
    public Loan(BookCopy copy, Member member) {
        this.bookCopy = copy;
        this.member = member;
        this.issueDate = LocalDate.now();
        this.dueDate = issueDate.plusDays(member.getLoanDays());
        copy.setStatus(BookStatus.BORROWED);
    }
    
    public double returnBook() {
        this.returnDate = LocalDate.now();
        bookCopy.setStatus(BookStatus.AVAILABLE);
        member.removeLoan(this);
        return calculateFine();
    }
    
    public double calculateFine() {
        if (returnDate == null || !returnDate.isAfter(dueDate)) return 0;
        long daysLate = ChronoUnit.DAYS.between(dueDate, returnDate);
        return daysLate * FINE_PER_DAY;
    }
}

// ========== Library ==========
class Library {
    private static Library instance;
    private Map<String, Book> booksByIsbn;
    private Map<String, Member> members;
    private List<Loan> allLoans;
    
    private Library() {
        booksByIsbn = new HashMap<>();
        members = new HashMap<>();
        allLoans = new ArrayList<>();
    }
    
    public static Library getInstance() {
        if (instance == null) instance = new Library();
        return instance;
    }
    
    public void addBook(Book book, int copies) {
        booksByIsbn.put(book.getIsbn(), book);
        for (int i = 0; i < copies; i++) book.addCopy();
    }
    
    public void registerMember(Member member) {
        members.put(member.id, member);
    }
    
    public Loan borrowBook(String memberId, String isbn) {
        Member member = members.get(memberId);
        Book book = booksByIsbn.get(isbn);
        
        if (member == null || book == null) return null;
        if (!member.canBorrow()) {
            System.out.println("Member has reached borrowing limit");
            return null;
        }
        
        BookCopy copy = book.getAvailableCopy();
        if (copy == null) {
            System.out.println("No copies available");
            return null;
        }
        
        Loan loan = new Loan(copy, member);
        member.addLoan(loan);
        allLoans.add(loan);
        System.out.println("Borrowed: " + book.getTitle());
        return loan;
    }
    
    public List<Book> searchByTitle(String title) {
        return booksByIsbn.values().stream()
            .filter(b -> b.getTitle().toLowerCase().contains(title.toLowerCase()))
            .collect(Collectors.toList());
    }
}

4Interview Follow-up Questions

Interview Follow-up Questions

Common follow-up questions interviewers ask

5Key Takeaways

1Separate Book (info) from BookCopy (physical item).
2Use inheritance for Member types (Student, Faculty).
3Encapsulate loan logic in Loan class.
4Consider reservations and notifications.
5Fine calculation should be in Loan class.
6Singleton for Library, Factory for member creation.