LLD Hub
lldoopobserver

Library Management System Low Level Design — LLD Interview Guide

Design a library system with book borrowing, fine calculation, and notifications. Classic beginner LLD problem asked in entry-level SDE interviews.

8 April 2025·7 min read

Practice this problem

Library Management System — get AI-scored feedback on your solution

Solve it →

Library Management System is a classic beginner-to-intermediate Low Level Design problem that covers entity design, fine calculation, and notification on overdue books. It is frequently asked at entry- to mid-level SDE interviews at product and service companies. This guide covers the complete Library Management LLD with Java code, class diagram, and FAQ.

Why Interviewers Ask Library Management LLD

The library problem is a test of clean entity modeling and business rule encoding. Interviewers want to see:

  • Can you model the relationship between Book, BookItem (physical copy), and Member cleanly?
  • Do you design a fine calculation rule as a Strategy, not hardcoded constants?
  • Do you track the borrowing state: BORROWED → RETURNED / OVERDUE?
  • Can you design a search function — by title, author, ISBN, category?
  • Do you use Observer to notify members when a reserved book becomes available?

Functional Requirements

  • Members can search for books by title, author, ISBN, or genre
  • Members can borrow a book for up to 14 days
  • Members can return a book — fine calculated if overdue
  • Members can reserve a book currently on loan — notified when available
  • Librarians can add/remove books and manage member accounts
  • A member can borrow at most 5 books simultaneously
  • System sends overdue reminders to members

Non-Functional Requirements

  • Fine calculation must be consistent and auditable — store fine breakdown per loan
  • Book availability must be accurate under concurrent borrow requests
  • Adding a new fine policy (e.g., tiered fines) must not change BorrowingService

Core Entities — Library Management LLD Class Design

  • Book — ISBN, title, author, publisher, genre, total copies
  • BookItem — barcode, book (FK), status (AVAILABLE/BORROWED/RESERVED/LOST)
  • Member — id, name, email, membership type, activeLoanCount
  • Loan — id, bookItem, member, borrowDate, dueDate, returnDate, fine
  • Reservation — member, book (not bookItem), reservedAt, status
  • FinePolicy — interface; DailyFinePolicy, TieredFinePolicy implement it
  • BorrowingService — borrow, return, calculate fine
  • NotificationService — Observer; notifies members of availability/overdue
  • Catalog — search books by multiple criteria

Text-Based Class Diagram

Book
+-- isbn: String (unique)
+-- title, author, publisher, genre: String
+-- items: List<BookItem>

BookItem
+-- barcode: String (unique)
+-- book: Book
+-- status: BookItemStatus (AVAILABLE/BORROWED/RESERVED/LOST)

Member
+-- id, name, email: String
+-- membershipType: MemberType (STUDENT/FACULTY/PUBLIC)
+-- activeLoanCount: int
+-- maxBorrowLimit: int  // derived from membershipType

Loan
+-- id: String
+-- bookItem: BookItem, member: Member
+-- borrowDate, dueDate, returnDate: LocalDate
+-- fine: double
+-- status: LoanStatus (ACTIVE/RETURNED/OVERDUE)

Reservation
+-- id, member: Member, book: Book
+-- reservedAt: LocalDateTime
+-- status: ReservationStatus (PENDING/FULFILLED/CANCELLED)

FinePolicy (interface)
+-- calculateFine(loan): double

DailyFinePolicy implements FinePolicy
TieredFinePolicy implements FinePolicy

Fine Calculation — Strategy Pattern

public interface FinePolicy {
    double calculateFine(Loan loan);
}

// Flat rate per day overdue
public class DailyFinePolicy implements FinePolicy {
    private final double ratePerDay;

    public DailyFinePolicy(double ratePerDay) {
        this.ratePerDay = ratePerDay;
    }

    @Override
    public double calculateFine(Loan loan) {
        if (loan.getReturnDate() == null || !loan.isOverdue()) return 0.0;
        long daysOverdue = ChronoUnit.DAYS.between(loan.getDueDate(), loan.getReturnDate());
        return Math.max(0, daysOverdue) * ratePerDay;
    }
}

// Tiered: higher rate after 7 days overdue
public class TieredFinePolicy implements FinePolicy {
    private static final double BASE_RATE = 2.0;
    private static final double HIGH_RATE = 5.0;
    private static final int TIER_THRESHOLD = 7;

    @Override
    public double calculateFine(Loan loan) {
        if (loan.getReturnDate() == null || !loan.isOverdue()) return 0.0;
        long overdueDays = ChronoUnit.DAYS.between(loan.getDueDate(), loan.getReturnDate());
        if (overdueDays <= 0) return 0.0;

        long tier1Days = Math.min(overdueDays, TIER_THRESHOLD);
        long tier2Days = Math.max(0, overdueDays - TIER_THRESHOLD);
        return (tier1Days * BASE_RATE) + (tier2Days * HIGH_RATE);
    }
}

BorrowingService — Borrow and Return

public class BorrowingService {
    private final BookItemRepository itemRepo;
    private final LoanRepository loanRepo;
    private final ReservationRepository reservationRepo;
    private final FinePolicy finePolicy;
    private final NotificationService notificationService;

    private static final int MAX_BORROW_DAYS = 14;

    public Loan borrowBook(String barcode, String memberId) {
        Member member = memberRepo.findById(memberId);
        if (member.getActiveLoanCount() >= member.getMaxBorrowLimit())
            throw new BorrowLimitExceededException("Member has reached borrow limit");

        BookItem item = itemRepo.findByBarcode(barcode);
        synchronized (item) { // prevent race on same item
            if (item.getStatus() != BookItemStatus.AVAILABLE)
                throw new BookNotAvailableException(barcode);

            item.setStatus(BookItemStatus.BORROWED);
            itemRepo.save(item);
        }

        member.incrementActiveLoanCount();
        memberRepo.save(member);

        LocalDate today = LocalDate.now();
        Loan loan = new Loan(UUID.randomUUID().toString(), item, member,
            today, today.plusDays(MAX_BORROW_DAYS), null, 0.0, LoanStatus.ACTIVE);
        return loanRepo.save(loan);
    }

    public Loan returnBook(String barcode, String memberId) {
        BookItem item = itemRepo.findByBarcode(barcode);
        Loan loan = loanRepo.findActiveByItemAndMember(barcode, memberId);

        loan.setReturnDate(LocalDate.now());
        double fine = finePolicy.calculateFine(loan);
        loan.setFine(fine);
        loan.setStatus(LoanStatus.RETURNED);
        loanRepo.save(loan);

        member.decrementActiveLoanCount();
        memberRepo.save(member);

        // Check if anyone reserved this book
        reservationRepo.findNextPending(item.getBook().getIsbn()).ifPresent(reservation -> {
            item.setStatus(BookItemStatus.RESERVED);
            notificationService.notifyAvailable(reservation.getMember(), item.getBook());
            reservation.setStatus(ReservationStatus.FULFILLED);
            reservationRepo.save(reservation);
        });

        if (item.getStatus() != BookItemStatus.RESERVED) {
            item.setStatus(BookItemStatus.AVAILABLE);
        }
        itemRepo.save(item);
        return loan;
    }
}

Catalog Search

public class Catalog {
    private final BookRepository bookRepo;

    public List<Book> searchByTitle(String title) {
        return bookRepo.findByTitleContainingIgnoreCase(title);
    }

    public List<Book> searchByAuthor(String author) {
        return bookRepo.findByAuthorContainingIgnoreCase(author);
    }

    public Optional<Book> searchByIsbn(String isbn) {
        return bookRepo.findByIsbn(isbn);
    }

    public List<Book> searchByGenre(String genre) {
        return bookRepo.findByGenre(genre);
    }

    public List<Book> getAvailableBooks() {
        return bookRepo.findBooksWithAvailableItems();
    }
}

Key Design Decisions

  • Book vs BookItem: A Book represents the logical title (one ISBN). A BookItem is a physical copy (one barcode, one borrowable unit). Without this distinction, the system cannot track which specific copy was borrowed or report accurate availability counts.
  • Fine as Strategy, not constant: Different member types may have different fine rates. Academic libraries have tiered fines. Encoding the rate as a constant inside Loan forces changes to Loan for every policy update. Strategy keeps Loan clean.
  • Synchronized on BookItem for borrow: Two librarians at different terminals could issue the same copy simultaneously. Synchronizing on the item object (not a global lock) scopes the contention to one copy without blocking unrelated borrow operations.
  • Reservation on Book, not BookItem: Members reserve a title, not a specific copy. On return, the system picks the next pending reservation for that book and assigns the returned copy to it. The member gets any available copy, not a specific one.

Common Follow-Up Questions

  • "How do you handle a member losing a book?" — Add a LOST status to BookItem. Charge a replacement fee (configurable per book). Decrement the member's active loan count. Update the library's total available copies.
  • "How do you send overdue reminders?" — A scheduled job runs daily. It queries Loan where status=ACTIVE and dueDate is before today. For each, it calls notificationService.sendOverdueReminder(loan). The NotificationService can send email, SMS, or push notifications (Observer pattern).
  • "How do you handle membership renewal and expiry?" — Add membershipExpiry to Member. BorrowingService checks validity before allowing borrow. Expired memberships cannot borrow but can return (to collect fines).

FAQ — Library Management System Low Level Design

What design patterns are used in Library Management LLD?

The primary patterns are Strategy (FinePolicy — DailyFinePolicy, TieredFinePolicy),Observer (notify members when a reserved book becomes available), andRepository (BookRepository, LoanRepository, ReservationRepository).

What is the difference between Book and BookItem?

A Book is the logical title — it has one ISBN, one author, and one genre. A BookItem is a physical copy of that book — it has a unique barcode, a status (AVAILABLE/BORROWED), and tracks which member currently has it. A library might have 5 BookItems for one Book.

How do you calculate fines in a library system?

On return, compute the number of days between dueDate and returnDate. If positive, multiply by the fine rate. Use a Strategy so the rate can vary by membership type or library policy. Store the calculated fine on the Loan record for auditing.

How do book reservations work?

A Reservation links a Member to a Book (not a specific copy) with a PENDING status. When any copy of that book is returned, the system checks for the oldest pending reservation and notifies that member. The copy is held in RESERVED status for 48 hours for that member to collect.

Ready to practice?

Submit your solution and get AI-scored feedback on OOP, SOLID principles, design patterns, and code quality.

Solve Library Management System