Sweaty Imposter

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.

All OOD Prompts