Design a Library Management System
OOP design for a library system covering book catalog management, member lending with borrow limits, fine calculation, and search functionality.
The Problem
Your university library still tracks loans on paper cards. Students lose track of due dates, librarians spend hours hunting for overdue books, and fine disputes happen weekly because nobody agrees on the return date. Last semester a student claimed a book was returned three weeks ago, but no record existed.
A library management system is a classic domain modeling problem. The core challenge is the distinction between a catalog entry (the book) and the physical copies that get checked out. The interviewer wants to see clean entity separation, borrow limit enforcement by member type, and a fine calculation strategy that is easy to extend.
Design the core classes for a library system that handles book catalog management, member lending with configurable borrow limits, fine calculation, and search.
Requirements
Clarifying Questions
Before jumping into class design, ask questions to turn the vague prompt into a concrete specification. Cover four areas: core actions, error handling, boundaries, and future extensions.
You: "Can the library have multiple physical copies of the same book?"
Interviewer: "Yes. A popular textbook might have 10 copies. Track each one independently."
That is the key insight. You need a Book (catalog entry) and a BookCopy (physical item). Now confirm member rules.
You: "Are there different types of members with different borrow limits?"
Interviewer: "Yes. Students can borrow up to 3 books, faculty can borrow up to 10."
Different limits per member type means you need a way to vary behavior by type. Keep this in mind for patterns.
You: "How are fines calculated? Is the policy the same for everyone?"
Interviewer: "Students pay $1 per day overdue. Faculty have no fines. We might add new policies later."
Multiple fine policies that vary by member type and might grow over time. That is a Strategy pattern signal.
You: "What happens if a member tries to borrow a book but has reached their limit?"
Interviewer: "Reject the request and tell them they have too many books checked out."
You: "Should we support reservations for books that are currently checked out?"
Interviewer: "Not in the base design, but mention it as an extension."
Good. That simplifies the core loan workflow. Now clarify search.
You: "What search capabilities do we need? Title, author, ISBN?"
Interviewer: "All three. Members should be able to search by title, author, or ISBN."
You: "Should the system send notifications when books are due or overdue?"
Interviewer: "Not required, but it is a great extension to discuss."
You: "Is thread safety a concern? Can two librarians process loans simultaneously?"
Interviewer: "Single-threaded for the base design. Mention concurrency as an extension."
Perfect. You have clarified scope and ruled out unnecessary complexity.
Final Requirements
Functional Requirements:
- The library maintains a catalog of books, each identified by ISBN, title, and author
- Each book can have multiple physical copies, each tracked independently
- Members borrow available copies and return them when done
- Students can borrow up to 3 books; faculty can borrow up to 10
- Fines are calculated per policy: students pay $1/day overdue, faculty pay nothing
- Members can search the catalog by title, author, or ISBN
Non-Functional Requirements:
- Adding a new member type or fine policy requires a new class, not changes to existing code
- The system enforces borrow limits before modifying any state
- Book copy status transitions are explicit and validated (no silent state corruption)
Out of Scope:
- Book reservations or hold queues
- Due date notifications
- Multi-threaded concurrent access
- Persistence or database storage
- UI rendering
- Payment processing for fines
Example Inputs and Outputs
Scenario 1: Successful borrow
- Input: Student "Alice" (0 books checked out) borrows ISBN "978-0134685991" (3 copies, 2 available)
- Expected: One copy assigned to Alice, her active loans increase to 1, copy status changes to CHECKED_OUT
- Why: Validates the happy path from search to checkout with borrow limit check
Scenario 2: Borrow limit exceeded
- Input: Student "Bob" (3 books checked out) tries to borrow ISBN "978-0201633610"
- Expected: Request rejected with message "Borrow limit reached (3/3)"
- Why: Validates that the system enforces limits before touching any state
Scenario 3: Return with fine
- Input: Student "Alice" returns a book that was due 5 days ago
- Expected: Copy status changes to AVAILABLE, fine of $5.00 calculated, Alice's active loans decrease by 1
- Why: Validates fine calculation and state cleanup on return
Scenario 4: Faculty return with no fine
- Input: Faculty "Dr. Smith" returns a book that was due 10 days ago
- Expected: Copy status changes to AVAILABLE, fine of $0.00 (faculty are exempt), active loans decrease by 1
- Why: Validates that fine policy varies by member type
Try It Yourself
Try it yourself
Before reading the solution, spend 15 minutes sketching a class diagram. Focus on the Book vs. BookCopy distinction and how you would enforce different borrow limits without if-else chains. Compare your approach with the walkthrough below.
Step 1: Identify Core Entities
Start by asking: what are the main "things" in this system? Look at your requirements and pull out the nouns. Each noun that has its own lifecycle or responsibilities becomes a candidate class.
A common mistake is lumping everything into a single Library god class. Good design means each class has a single, clear job.
| Entity | Responsibility | Key attributes |
|---|---|---|
| Library | The orchestrator. Manages catalog, members, and coordinates borrow/return. | catalog, members |
| Book | Catalog entry. Holds metadata and knows which copies exist. | isbn, title, author, copies |
| BookCopy | Physical item. Tracks its own status and current borrower. | copyId, status, dueDate |
| Member | Abstract base. Holds identity and active loans list. | memberId, name, activeLoans |
| StudentMember | Concrete member with 3-book limit and daily fine policy. | borrowLimit = 3 |
| FacultyMember | Concrete member with 10-book limit and no-fine policy. | borrowLimit = 10 |
| Loan | Value object linking a member, a copy, and dates. | member, copy, borrowDate, dueDate |
| FineCalculator | Strategy interface. Each policy computes fines differently. | calculateFine(loan) |
| SearchService | Handles catalog queries by title, author, or ISBN. | search(query) |
Notice that Book and BookCopy are separate because they have different lifecycles. A Book exists permanently in the catalog. A BookCopy transitions between AVAILABLE, CHECKED_OUT, and LOST. Merging them would violate SRP because catalog metadata and physical tracking are different responsibilities.
We also separated Member into StudentMember and FacultyMember because they have different borrow limits and fine policies. An abstract Member base class captures shared behavior (identity, loan tracking) while subclasses define the type-specific rules.
Step 2: Define Relationships and Class Design
Library (the orchestrator)
The Library class coordinates everything. It owns the catalog and member registry, but delegates domain logic to the entities themselves.
Deriving state and methods from requirements:
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with NotesFromSDE Premium.