Home/Learn/Low Level Design/Design a Library Management System

Design a Library Management System

Intermediate
LLD Interview Problems
Java Source Code

Model a library that manages physical book copies, member loans, reservations, and automated overdue fine calculation.

Overview

A Library Management System tracks two distinct concepts: Book (metadata — title, ISBN, author) and BookItem (a physical copy with its own barcode and availability status). Members hold a LibraryCard, borrow BookItems, and receive Fine notifications when overdue. The Catalog supports searching by title, author, or ISBN. Reservations queue members when all copies are checked out. Observer fires overdue alerts. Strategy drives pluggable fine calculation (daily flat rate vs. tiered). Factory creates member subtypes (Standard, Premium) with different borrow limits.

Requirements Analysis

Functional: search catalog, checkout a BookItem, return a BookItem, reserve a book, calculate fine for overdue loans, notify members of overdue items. Non-functional: consistent loan state (no double-checkout), extensible fine rules without touching Library core, support high catalog search load via indexed Catalog.

Requirements
// Entities : Library, Book, BookItem, Member, LibraryCard, Loan, Fine, Catalog, Reservation
// Patterns : Observer (overdue alerts), Strategy (fine calc), Factory (member types)

Core Classes & Relationships

BookStatus enum: AVAILABLE, LOANED, RESERVED, LOST. Loan links a Member to a BookItem with issue and due dates. FineStrategy interface has two implementations: FlatRateFine and TieredFine. Library owns a Catalog and a list of active Loans. OverdueNotifier implements Observer and is registered with a scheduled job.

Java — enums & interfaces
public enum BookStatus { AVAILABLE, LOANED, RESERVED, LOST }

public interface FineStrategy {
    double calculate(long overdueDays);
}

public class FlatRateFine implements FineStrategy {
    private final double ratePerDay;
    public FlatRateFine(double rate) { this.ratePerDay = rate; }
    @Override public double calculate(long days) { return days * ratePerDay; }
}

public class TieredFine implements FineStrategy {
    @Override public double calculate(long days) {
        if (days <= 7)  return days * 1.0;
        if (days <= 30) return 7 + (days - 7) * 2.0;
        return 7 + 46 + (days - 30) * 5.0;
    }
}

public interface OverdueObserver {
    void onOverdue(Member member, Loan loan);
}

Java Implementation

BookItem wraps a Book and tracks its own status. Loan records issue and due dates; isOverdue() computes against LocalDate.now(). Library.checkoutBook() validates member borrow limit, changes BookItem status, creates a Loan, and persists it. Library.returnBook() calculates overdue days, applies the FineStrategy, notifies observers, and resets status.

Java — core classes
public class Book {
    private final String isbn;
    private final String title;
    private final String author;
    public Book(String isbn, String title, String author) {
        this.isbn = isbn; this.title = title; this.author = author;
    }
    public String getIsbn() { return isbn; }
}

public class BookItem {
    private final String barcode;
    private final Book book;
    private BookStatus status = BookStatus.AVAILABLE;

    public BookItem(String barcode, Book book) { this.barcode = barcode; this.book = book; }
    public boolean isAvailable()          { return status == BookStatus.AVAILABLE; }
    public void setStatus(BookStatus s)   { this.status = s; }
    public Book getBook()                 { return book; }
    public String getBarcode()            { return barcode; }
}

public class Loan {
    private final Member member;
    private final BookItem bookItem;
    private final LocalDate issueDate;
    private final LocalDate dueDate;

    public Loan(Member member, BookItem bookItem, int loanDays) {
        this.member   = member;
        this.bookItem = bookItem;
        this.issueDate = LocalDate.now();
        this.dueDate   = issueDate.plusDays(loanDays);
    }
    public boolean isOverdue()   { return LocalDate.now().isAfter(dueDate); }
    public long overdueDays()    { return ChronoUnit.DAYS.between(dueDate, LocalDate.now()); }
    public BookItem getBookItem(){ return bookItem; }
    public Member getMember()    { return member; }
}

public class Library {
    private final Catalog catalog;
    private final List<Loan> activeLoans = new ArrayList<>();
    private final FineStrategy fineStrategy;
    private final List<OverdueObserver> observers = new ArrayList<>();
    private static final int DEFAULT_LOAN_DAYS = 14;

    public Library(Catalog catalog, FineStrategy fineStrategy) {
        this.catalog = catalog; this.fineStrategy = fineStrategy;
    }
    public void addObserver(OverdueObserver o) { observers.add(o); }

    public Loan checkoutBook(String isbn, Member member) {
        BookItem item = catalog.findAvailable(isbn)
            .orElseThrow(() -> new IllegalStateException("No available copy for ISBN " + isbn));
        if (!member.canBorrow())
            throw new IllegalStateException("Member has reached borrow limit");
        item.setStatus(BookStatus.LOANED);
        Loan loan = new Loan(member, item, DEFAULT_LOAN_DAYS);
        activeLoans.add(loan);
        member.addLoan(loan);
        return loan;
    }

    public double returnBook(Loan loan) {
        double fine = 0;
        if (loan.isOverdue()) {
            fine = fineStrategy.calculate(loan.overdueDays());
            observers.forEach(o -> o.onOverdue(loan.getMember(), loan));
        }
        loan.getBookItem().setStatus(BookStatus.AVAILABLE);
        activeLoans.remove(loan);
        loan.getMember().removeLoan(loan);
        return fine;
    }
}

Key Points to Remember

  • 1Separate Book (metadata) from BookItem (physical copy) — one ISBN maps to many barcodes, each with independent status.
  • 2Strategy for fine calculation means adding a weekend-surcharge rule is a new class, not a modification of Library.
  • 3Observer decouples overdue detection from notification channels — email, SMS, and push can all be separate observers.
  • 4Member.canBorrow() encapsulates the borrow-limit rule so Library never hard-codes member type logic.

Interview Questions

Sign in to ask Aria
1

How would you handle the reservation queue when a returned book has multiple waiting members?

MediumAmazon
2

How would you design the Catalog to support full-text search across millions of books efficiently?

HardGoogle
3

How do you prevent a race condition where two members try to checkout the last copy simultaneously?

HardMicrosoft

Ask Aria about Design a Library Management System

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…