Overview
Design a library management system. Requirements: the catalog has books (title, author, ISBN) with multiple physical copies; members can search, borrow up to a limit, return, and reserve books; loans have due dates and late fines; librarians add books and copies; a member with unpaid fines above a threshold cannot borrow.
The central modeling insight is separating Book (catalog entry) from BookCopy (physical item with barcode and status). Loans link a member and a copy with dates. Reservations form a FIFO queue per book; when a copy is returned, the next reserver is notified (Observer) and the copy is held for them. Fine calculation is a strategy that may differ by member type.
The catalog card describes a book; the shelves hold several physical copies. The desk stamps a due date on your card, and if someone reserved the book, the returned copy goes to the hold shelf with their name on it.
When to use it
- Classic LLD interview problem for entity modeling.
- Practicing type-object separation and business rules.
- Reservation queues and notifications.
Where it shows up in interviews
Recognize it when: one item type with many physical units.
- Design a library system
- Design a car rental system
- Design an inventory system
Recognize it when: queue users for unavailable items.
- Design a library system
- Design a restaurant waitlist
Where it is used in real software
Koha and Evergreen model bibliographic records separately from item records with barcodes.
Products (SKU) vs stock units in warehouses follow the same pattern.
Public libraries place holds in FIFO order and notify patrons when items are ready.
Key terms
- Book
- Catalog entry: ISBN, title, authors.
- BookCopy
- Physical item with barcode and status.
- Loan
- Member + copy + borrow date + due date + return date.
- Reservation
- Member waiting for a book (not a specific copy).
- FinePolicy
- Strategy computing late fees.
How it works, step by step
- 1Clarify
Borrow limits, loan period, fines, reservations, search fields, roles.
- 2Entities
Library, Book, BookCopy, Member, Loan, Reservation, FinePolicy, Notifier.
- 3Checkout rules
Member active, under limit, no big fines, copy available or held for this member.
- 4Return flow
Close loan, compute fine, hand copy to next reservation or mark available.
- 5Search
Index by title, author, ISBN.
Checkout validation
Limit 3 loans, max fines 10.00
| Member situation | Result | Rule |
|---|---|---|
| 2 loans, no fines, copy available | Allowed | - |
| 3 loans | Rejected | Borrow limit reached |
| Fines 12.50 | Rejected | Unpaid fines over threshold |
| Copy on hold for another member | Rejected | Reserved for next in queue |
| Copy on hold for this member | Allowed | Hold fulfilled |
NOWMember situation: 2 loans, no fines, copy available | Result: Allowed | Rule: -
All rules live in one checkout method on the Library (facade), with data owned by the entities.
Implementation
import java.time.*;import java.util.*; record Book(String isbn, String title, String author) {} final class BookCopy { enum Status { AVAILABLE, ON_LOAN, ON_HOLD, LOST } final String barcode; final Book book; Status status = Status.AVAILABLE; String heldFor; // member id when ON_HOLD BookCopy(String barcode, Book book) { this.barcode = barcode; this.book = book; }} final class Member { final String id; final String name; final List<Loan> activeLoans = new ArrayList<>(); long finesCents; Member(String id, String name) { this.id = id; this.name = name; }} final class Loan { final BookCopy copy; final Member member; final LocalDate borrowed, due; LocalDate returned; Loan(BookCopy copy, Member member, LocalDate borrowed, LocalDate due) { this.copy = copy; this.member = member; this.borrowed = borrowed; this.due = due; }} interface FinePolicy { long fineCents(Loan loan, LocalDate returnedOn); }final class PerDayFine implements FinePolicy { private final long perDay, cap; PerDayFine(long perDay, long cap) { this.perDay = perDay; this.cap = cap; } public long fineCents(Loan l, LocalDate on) { return Math.min(cap, Math.max(0, Duration.between(l.due.atStartOfDay(), on.atStartOfDay()).toDays()) * perDay); }} interface Notifier { void holdReady(Member m, Book b); } final class Library { private static final int MAX_LOANS = 3, LOAN_DAYS = 14; private static final long MAX_FINES = 1_000; private final Map<String, Book> books = new HashMap<>(); private final Map<String, List<BookCopy>> copiesByIsbn = new HashMap<>(); private final Map<String, BookCopy> copiesByBarcode = new HashMap<>(); private final Map<String, Deque<Member>> reservations = new HashMap<>(); private final Map<String, Loan> loansByBarcode = new HashMap<>(); private final FinePolicy fines; private final Notifier notifier; private final Clock clock; Library(FinePolicy fines, Notifier notifier, Clock clock) { this.fines = fines; this.notifier = notifier; this.clock = clock; } void addCopy(Book book, String barcode) { books.putIfAbsent(book.isbn(), book); BookCopy copy = new BookCopy(barcode, book); copiesByIsbn.computeIfAbsent(book.isbn(), k -> new ArrayList<>()).add(copy); copiesByBarcode.put(barcode, copy); } List<Book> search(String text) { String q = text.toLowerCase(); return books.values().stream().filter(b -> b.title().toLowerCase().contains(q) || b.author().toLowerCase().contains(q) || b.isbn().equals(text)).toList(); } Loan checkout(Member m, String isbn) { if (m.activeLoans.size() >= MAX_LOANS) throw new IllegalStateException("Borrow limit reached"); if (m.finesCents > MAX_FINES) throw new IllegalStateException("Unpaid fines over limit"); BookCopy copy = copiesByIsbn.getOrDefault(isbn, List.of()).stream() .filter(c -> c.status == BookCopy.Status.AVAILABLE || (c.status == BookCopy.Status.ON_HOLD && m.id.equals(c.heldFor))) .findFirst().orElseThrow(() -> new IllegalStateException("No copy available; reserve instead")); LocalDate today = LocalDate.now(clock); Loan loan = new Loan(copy, m, today, today.plusDays(LOAN_DAYS)); copy.status = BookCopy.Status.ON_LOAN; copy.heldFor = null; m.activeLoans.add(loan); loansByBarcode.put(copy.barcode, loan); return loan; } long returnCopy(String barcode) { Loan loan = Optional.ofNullable(loansByBarcode.remove(barcode)).orElseThrow(() -> new IllegalArgumentException("Not on loan")); LocalDate today = LocalDate.now(clock); loan.returned = today; loan.member.activeLoans.remove(loan); long fine = fines.fineCents(loan, today); loan.member.finesCents += fine; BookCopy copy = loan.copy; Deque<Member> queue = reservations.getOrDefault(copy.book.isbn(), new ArrayDeque<>()); Member next = queue.poll(); if (next != null) { copy.status = BookCopy.Status.ON_HOLD; copy.heldFor = next.id; notifier.holdReady(next, copy.book); } else { copy.status = BookCopy.Status.AVAILABLE; } return fine; } void reserve(Member m, String isbn) { if (!books.containsKey(isbn)) throw new IllegalArgumentException("Unknown ISBN"); Deque<Member> q = reservations.computeIfAbsent(isbn, k -> new ArrayDeque<>()); if (q.contains(m)) throw new IllegalStateException("Already reserved"); q.add(m); }}Complexity and performance
Small.
Hash lookups.
Use an inverted index at scale.
Trade-offs
Reserving the book (any copy) serves members faster than reserving a specific copy.
Computing on return is simple; a daily job lets you block members with overdue items before they return them.
Variants and related techniques
Students vs faculty with different limits and fine policies (Strategy).
E-books with license counts instead of physical copies.
Common mistakes
- Book and copy merged into one class.
Fix: Separate catalog entries from physical items.
- Returned copy made available despite a reservation queue.
Fix: Check the queue and place the copy on hold.
- Rules spread across UI and entities.
Fix: Centralize in the Library service with entities owning their state.
Interview questions
Why separate Book and BookCopy?
A title can have many physical copies with different statuses and barcodes. Loans and holds apply to copies, while search and reservations apply to books.
How do reservations work when a copy is returned?
The return flow checks the book's FIFO reservation queue; if someone is waiting, the copy is placed on hold for them and they are notified, otherwise it becomes available.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Library with borrow limits and fines | Medium | Rules. |
| Add reservations with hold expiry | Hard | Queues and timers. |