Library Check-out System
Design a library system that tracks books, members, check-outs, holds, and fines. A single title may have many physical copies; members can place holds when all copies are checked out.
Clarifying Questions
- Do we distinguish a logical Book (title/ISBN) from a physical Copy?
- How are holds resolved: FIFO queue, priority by member tier?
- Are fines computed on return, daily by a job, or both?
- What member states exist (active, suspended, expired) and how do they gate borrowing?
- Do we support inter-branch transfers or is each branch siloed?
Core Requirements
- Catalog distinct titles and their physical copies, each with a location.
- Check copies in and out to members with due dates.
- Place and resolve holds when no copy is available.
- Accrue and collect fines for overdue returns.
- Enforce per-member borrowing limits and suspended states.
- Search the catalog by title, author, or subject.
Canonical class breakdown
Book: Logical title-level record (ISBN, author, subject); does not represent a physical item.
BookCopy: A physical, checkoutable instance of a Book with a barcode, location, and current status.
Member: Library patron with status, current loans, holds, and outstanding fines.
Loan: Open transaction linking a BookCopy to a Member with checkout date and due date.
Hold: Reservation against a Book (not a Copy) sitting in a per-title FIFO queue.
Catalog: Search index over Books; abstracts the underlying store so search can be swapped for ES later.
FinePolicy (interface): Computes overdue fines per day; concrete impls vary by member tier or item type.
LibraryService: Orchestrates checkout, return, hold placement, and hold fulfillment; the use-case layer.
Java sketchjava
// Java skeleton
enum CopyStatus { AVAILABLE, CHECKED_OUT, ON_HOLD_SHELF, LOST }
enum MemberStatus { ACTIVE, SUSPENDED, EXPIRED }
class Book {
private final String isbn;
private final String title;
private final String author;
private final Set<String> subjects;
}
class BookCopy {
private final String barcode;
private final Book book;
private String location;
private CopyStatus status;
}
class Member {
private final String memberId;
private MemberStatus status;
private final List<Loan> activeLoans;
private final List<Hold> holds;
private BigDecimal outstandingFines;
}
class Loan {
private final String loanId;
private final BookCopy copy;
private final Member member;
private final LocalDate checkoutDate;
private LocalDate dueDate;
private LocalDate returnedDate;
}
class Hold {
private final String holdId;
private final Book book; // attaches to title, not copy
private final Member member;
private final Instant placedAt;
}
class Catalog {
public List<Book> searchByTitle(String q) { ... }
public List<Book> searchByAuthor(String q) { ... }
public List<Book> searchBySubject(String q) { ... }
}
interface FinePolicy {
BigDecimal computeFine(Loan loan, LocalDate asOf);
}
class LibraryService {
private final Catalog catalog;
private final FinePolicy finePolicy;
private final Map<String, Queue<Hold>> holdsByIsbn;
public Loan checkout(Member m, BookCopy copy) { ... }
public BigDecimal returnCopy(BookCopy copy) { /* fine + fulfill next hold */ }
public Hold placeHold(Member m, Book book) { ... }
public Loan fulfillNextHold(Book book, BookCopy returned) { ... }
}
Key Decisions to Defend
- Book vs BookCopy is the single most important split: holds attach to Book, loans attach to Copy.
- Holds are FIFO on the Book, not the Copy, because any returned copy can satisfy the next hold.
- FinePolicy as Strategy because grace periods and tier rules are the most-changed rules in a real library.
- Status on BookCopy is an enum, not a state machine class, transitions are simple and exhaustive.
- Catalog search is its own port so you can move from in-memory to a real index without touching domain.
Likely follow-ups
- · Add inter-branch transfers, where does the in-transit state live?
- · Support digital loans with hard expiry, same model or separate?
- · How would you generate the nightly overdue notice job without N+1 queries?
Dive Deeper
Tests whether you can separate logical identity (the Book) from physical identity (the Copy). Candidates who conflate them end up with broken hold semantics and impossible-to-reconcile inventory. Also probes whether you treat policy (fines, hold ordering) as pluggable, because library rules genuinely change every budget cycle.