Command Palette

Search for a command to run...

PHASE 4Intermediate ~8 min· topic 4 of 18Level 1

Problem 4.4 — Library Management

In one line

Entities, borrow/return flows, fine calculation and search — the 'clean CRUD with rules' LLD problem.

0/18 · 0%

Key ideas

  1. 01

    In plain words: a library has books, and each book might have several physical copies. A member borrows one copy at a time, must return it within a deadline, and pays a fine if they're late. The trick most people miss: 'Harry Potter' the title and 'this specific copy on the shelf' are two different things.

  2. 02

    Entities: Book (ISBN, title, author), BookCopy (id, condition), Member, Loan, FineCalculator.

  3. 03

    Loan lifecycle: borrow (copy → checked out) → return (fine if overdue) → renew.

  4. 04

    One BookCopy can be on one active Loan — the invariant to keep thread-safe.

  5. 05

    Fine: strategy by member type (student/staff) and book category, per-day rate.

  6. 06

    Search: by title/author/ISBN — index-friendly; keep the search behind a port (later: Elastic/DB full-text).

  7. 07

    Reservations/holds add the next LLD layer: queue per book, notify on availability (Observer).

  8. 08

    Extensibility: new fine rule, new search engine, new media type (DVD) each hits one seam.

Java / Spring map

  • →

    BorrowService with @Transactional loan creation; LoanRepository with a unique partial index on active loans.

Code & diagrams

LibraryBorrow.javajava

Book vs BookCopy, a fine strategy, and the borrow flow that protects the 'one active loan per copy' rule.

public record Book(String isbn, String title, String author) {}

public final class BookCopy {
  private final String copyId;
  private final String isbn;
  private boolean onLoan = false;
  public BookCopy(String copyId, String isbn) { this.copyId = copyId; this.isbn = isbn; }
  public synchronized boolean checkout() {          // atomic claim, same idea as ParkingSpot
    if (onLoan) return false;
    onLoan = true;
    return true;
  }
  public synchronized void checkin() { onLoan = false; }
  public String copyId() { return copyId; }
}

public interface FinePolicy { Money finePerDay(); Duration loanPeriod(); }
public record StudentFinePolicy() implements FinePolicy {
  public Money finePerDay() { return new Money(1000, "INR"); }  // ₹10/day
  public Duration loanPeriod() { return Duration.ofDays(14); }
}

public final class Loan {
  private final BookCopy copy;
  private final Instant dueAt;
  private Instant returnedAt;
  private final FinePolicy policy;

  public Loan(BookCopy copy, Instant borrowedAt, FinePolicy policy) {
    this.copy = copy; this.policy = policy;
    this.dueAt = borrowedAt.plus(policy.loanPeriod());
  }

  public Money returnBook(Instant now) {                    // "tell, don't ask": Loan computes its own fine
    this.returnedAt = now;
    copy.checkin();
    long lateDays = Math.max(0, Duration.between(dueAt, now).toDays());
    return new Money(policy.finePerDay().minor() * lateDays, "INR");
  }
}

public final class LibraryService {
  public Loan borrow(BookCopy copy, FinePolicy policy, Instant now) {
    if (!copy.checkout()) throw new IllegalStateException("copy already on loan: " + copy.copyId());
    return new Loan(copy, now, policy);
  }
}

Explain without notes

01

How do you prevent two members from borrowing the same copy simultaneously in a real (multi-instance) app?

Practice

01

Add Reservation + waitlist with queue; add 'lost book' flow where the borrower pays replacement cost.

Trade-offs

  • ↔

    Fines config in DB vs code: DB = easy ops change, code = compile-time safety. Pick one and justify.

Completion checklist

  • I can model Book/BookCopy/Member/Loan with correct multiplicities and invariants, and can explain why Book and BookCopy are two different classes.

Back to phase