Definition
- Book – the abstract title with ISBN, author, and name. Represents the concept, not a physical copy.
- BookItem – a physical copy with its own barcode, status (AVAILABLE, ISSUED, RESERVED), and due date.
- Member – a library member who can hold several issued items up to a configured limit.
- Reservation – a queue entry for a member waiting for a title whose copies are all out.
- Fine – computed by a
FinePolicywhen a book item is returned overdue. - LibrarianService – orchestrates issue, return, renewal, and reservation operations.
Think of National Bank of India lending out physical gold certificates. The certificate design (Book) is one thing, but each printed copy (BookItem) is a separate physical object with its own barcode and status. Rajesh borrows a copy, Priya reserves one that is out, and if Rajesh returns late he pays a fine, exactly like a late fee on a loan repayment.
Understanding the Concepts
What are the core entities?
The key modelling insight is the separation of Book from BookItem.
package com.bank.library.model;
import java.util.List;
import java.util.ArrayList;
public class Book {
private final String isbn;
private final String title;
private final String author;
private final List<BookItem> copies;
public Book(String isbn, String title, String author) {
this.isbn = isbn;
this.title = title;
this.author = author;
this.copies = new ArrayList<>();
}
public void addCopy(BookItem item) { copies.add(item); }
public List<BookItem> getCopies() { return copies; }
public String getIsbn() { return isbn; }
public String getTitle() { return title; }
public String getAuthor() { return author; }
}
package com.bank.library.model;
import java.time.LocalDate;
public class BookItem {
private final String barcode;
private BookItemStatus status;
private LocalDate dueDate;
private String issuedToMemberId;
public BookItem(String barcode) {
this.barcode = barcode;
this.status = BookItemStatus.AVAILABLE;
}
public void issue(String memberId, LocalDate dueDate) {
if (status != BookItemStatus.AVAILABLE) {
throw new IllegalStateException(
"Item " + barcode + " is not available");
}
this.status = BookItemStatus.ISSUED;
this.issuedToMemberId = memberId;
this.dueDate = dueDate;
}
public void returnItem() {
this.status = BookItemStatus.AVAILABLE;
this.issuedToMemberId = null;
this.dueDate = null;
}
public boolean isOverdue() {
return status == BookItemStatus.ISSUED
&& dueDate != null
&& LocalDate.now().isAfter(dueDate);
}
public String getBarcode() { return barcode; }
public BookItemStatus getStatus() { return status; }
public LocalDate getDueDate() { return dueDate; }
}
package com.bank.library.model;
public enum BookItemStatus {
AVAILABLE, ISSUED, RESERVED, LOST
}
How does issue work?
Issuing finds a free BookItem for the requested ISBN, marks it ISSUED, and records a due date.
package com.bank.library.service;
import com.bank.library.model.*;
import java.time.LocalDate;
import java.util.Optional;
public class LibrarianService {
private static final int LOAN_DAYS = 14;
private static final int MAX_ITEMS_PER_MEMBER = 5;
private final FinePolicy finePolicy;
public LibrarianService(FinePolicy finePolicy) {
this.finePolicy = finePolicy;
}
public BookItem issue(Member member, Book book) {
if (member.getIssuedCount() >= MAX_ITEMS_PER_MEMBER) {
throw new IllegalStateException(
member.getName() + " has reached the issue limit");
}
Optional<BookItem> free = book.getCopies().stream()
.filter(c -> c.getStatus() == BookItemStatus.AVAILABLE)
.findFirst();
if (free.isEmpty()) {
throw new IllegalStateException(
"No copies available for " + book.getTitle());
}
BookItem item = free.get();
item.issue(member.getMemberId(),
LocalDate.now().plusDays(LOAN_DAYS));
member.addIssuedItem(item);
System.out.println("Issued " + item.getBarcode()
+ " to " + member.getName()
+ ", due " + item.getDueDate());
return item;
}
}
How does return and fine calculation work?
Returning marks the copy AVAILABLE and computes a fine if overdue.
package com.bank.library.service;
import java.math.BigDecimal;
public interface FinePolicy {
BigDecimal computeFine(long overdueDays);
}
package com.bank.library.service;
import java.math.BigDecimal;
public class FlatFinePolicy implements FinePolicy {
private final BigDecimal perDayFine;
public FlatFinePolicy(BigDecimal perDayFine) {
this.perDayFine = perDayFine;
}
@Override
public BigDecimal computeFine(long overdueDays) {
return perDayFine.multiply(BigDecimal.valueOf(overdueDays));
}
}
// In LibrarianService:
public BigDecimal returnItem(Member member, BookItem item) {
BigDecimal fine = BigDecimal.ZERO;
if (item.isOverdue()) {
long overdueDays = java.time.temporal.ChronoUnit.DAYS.between(
item.getDueDate(), java.time.LocalDate.now());
fine = finePolicy.computeFine(overdueDays);
System.out.println("Fine for " + member.getName()
+ ": " + fine + " (" + overdueDays + " days overdue)");
}
item.returnItem();
member.removeIssuedItem(item);
return fine;
}
How does renewal work?
Renewal extends the due date only when no other member has reserved the title.
public void renew(BookItem item, Book book) {
if (hasReservation(book)) {
throw new IllegalStateException(
"Cannot renew: " + book.getTitle()
+ " has pending reservations");
}
item.setDueDate(java.time.LocalDate.now().plusDays(14));
System.out.println("Renewed " + item.getBarcode()
+ ", new due date: " + item.getDueDate());
}
How does reservation work?
When all copies are out, a member can reserve the title. When a copy returns, the first member in the queue is notified.
package com.bank.library.model;
import java.util.LinkedList;
import java.util.Queue;
public class ReservationQueue {
private final Queue<Member> queue = new LinkedList<>();
public void reserve(Member member) {
queue.add(member);
System.out.println(member.getName()
+ " added to reservation queue");
}
public Member notifyNext() {
Member next = queue.poll();
if (next != null) {
System.out.println("Notifying " + next.getName()
+ ": your reserved book is available");
}
return next;
}
public boolean hasReservations() {
return !queue.isEmpty();
}
}
How does search work?
Search is served by indexes keyed on title, author, and ISBN so lookups are fast.
package com.bank.library.service;
import com.bank.library.model.Book;
import java.util.*;
public class BookCatalog {
private final Map<String, Book> byIsbn = new HashMap<>();
private final Map<String, List<Book>> byAuthor = new HashMap<>();
public void addBook(Book book) {
byIsbn.put(book.getIsbn(), book);
byAuthor.computeIfAbsent(book.getAuthor().toLowerCase(),
k -> new ArrayList<>()).add(book);
}
public Book findByIsbn(String isbn) {
return byIsbn.get(isbn);
}
public List<Book> findByAuthor(String author) {
return byAuthor.getOrDefault(
author.toLowerCase(), List.of());
}
}
How do you handle concurrency?
Two members acting on the same title at the same instant must be serialised, otherwise a single copy could be issued twice. Use per-title locking.
// Synchronize on the book object for per-title locking
public BookItem issue(Member member, Book book) {
synchronized (book) {
// find free copy and issue, thread-safe
}
}
What should a senior engineer know?
- The Book vs BookItem separation is the single most important modelling decision. Without it, multi-copy lending is incorrect.
- Fine policy as a Strategy lets the library change from flat to tiered fines without touching lending logic.
- Reservations use an observer-style notification queue.
- The LOST status handles the case where a member never returns a copy, triggering a replacement fee.
What are common interview follow-up questions?
- How would you add a new fine policy without changing existing code?
- How do you prevent two members from getting the same copy?
- How would you add an eBook lending feature?
- What happens if a member loses a book?
- How do you track transaction history for audit?
Visual Overview
Book vs BookItem: ───────────────── Book (ISBN: 978-0134685991) │ ├── BookItem (barcode: LIB-001, AVAILABLE) ├── BookItem (barcode: LIB-002, ISSUED to Rajesh) └── BookItem (barcode: LIB-003, RESERVED)
Issue Flow:
───────────
Member requests ISBN
│
▼
Find free BookItem for that ISBN
│
▼
BookItem.issue(memberId, dueDate)
│
▼
Member.addIssuedItem(item)
│
▼
Return Ticket / Confirmation
Return Flow:
────────────
Member returns BookItem
│
▼
Is overdue? ──yes──→ FinePolicy.computeFine(days)
│ │
│ no ▼
▼ Charge fine
BookItem.returnItem()
│
▼
Reservation? ──yes──→ Notify next member
│
│ no
▼
Item available for anyone
Key Takeaway
When you design a library management system, the single most important modelling decision is separating the Book from the BookItem. A Book is the abstract title with its ISBN, author, and name, while a BookItem is one physical copy with its own barcode, status, and due date. A book has many items, so when a member borrows, you are lending a specific copy, not the abstract title, and this separation is what makes multi-copy lending correct.
The main operations are issue, return, renewal, and reservation. Issuing finds an available copy, marks it issued with a due date, and adds it to the member's issued list. Returning frees the copy and charges a fine if overdue. Renewal extends the due date but only when no one has reserved the title. Reservations use an observer-style queue: when a copy returns, the first waiting member is notified.
Fine calculation is pulled into a fine-policy interface so the library can move from a flat per-day fine to a tiered one without touching the lending logic. This is the Open/Closed Principle and the Strategy pattern working together. Search is backed by indexes on title, author, and ISBN for fast lookups.
The concurrency concern is that two members acting on the same title at the same instant must be serialised, otherwise a single copy could be double-issued. Per-title locking solves this. In banking at National Bank of India, this is the same discipline applied to lending any limited physical asset: distinguish the design from the instance, track status transitions precisely, and charge late fees through a replaceable policy.
Remember one sentence: a library separates the abstract Book from the physical BookItem, lends specific copies with due dates, and charges fines through a pluggable policy.
---