Library System

By the Syncfusion DevTutorials team · Last updated

Design a library: copies, loans with due days and fines, a three-book limit and a fair waiting list, built against hidden tests.

QuickBite has a small office library with cookery and technology books. The library currently uses a notebook to record who borrowed each book, when it is due, and who is waiting for it.

Managing these details by hand can lead to missing records, incorrect due dates, and confusion about whose turn is next.

In this case study, you will design a simple library management system for borrowing and returning books, tracking due dates, calculating fines, and maintaining a fair waiting list.

In simple words: you will replace the notebook with three small classes. Each class will have a clear responsibility, and automated tests will check whether the library rules work correctly.

What You Will Learn

In this case study, you will:

  1. Tell a book title apart from its copies.
  2. Record each loan with its due day and fine.
  3. Check borrowing rules in a clear order.
  4. Keep a fair, first-come waiting list.
  5. Build the library in the sandbox and pass the hidden tests.

Library, hotel and movie-rental questions are all the same shape: things, people, and a record of who has what until when. Spot that shape and the classes follow.

Step 1: Agree on the Requirements

Before designing classes or writing code, clearly define what the system must do.

In a system design interview, explain these requirements to the interviewer and check that the scope is correct. This helps avoid building features that are not required.

What the System Must Do

The library system must support the following operations:

  • addBook(isbn, title, copies) adds a book and its available copies to the library.
  • borrow(member, isbn, day) lends an available copy to a member. The copy is due 14 days after it is borrowed.
  • A member can borrow no more than three books at a time.
  • A member cannot borrow two copies of the same book.
  • giveBack(member, isbn, day) records the return and calculates a fine of 10 for each day the book is late.
  • reserve(member, isbn) adds a member to the waiting list when no copy is available.
  • When a copy is returned, it is reserved for the member who has waited the longest.

Here, isbn is the unique number used to identify a book title, while day is the day number used to calculate due dates and fines.

What the System Will Not Include

To keep this case study focused, the first version will not include:

  • Searching the library catalogue
  • Renewing a loan
  • Paying fines online

These features can be added later. Leaving them out now keeps the design small enough to understand, build, and test.

Step 2: Find the Classes

A simple trick: underline the nouns in the requirements. Most of them become classes. The verbs become methods.

ClassIts one job
BookKnows the title, the number of free copies and the waiting list
LoanRemembers the member, the book and the due day
LibraryChecks the rules, lends and takes back books

The class diagram for this design.

Step 3: Build the Key Parts

A Book Is Not a Copy

The library has one title, "Clean Code", but three physical copies. The title is the same for everyone; the number of free copies changes.

The picture shows one title with three copies, after one copy is lent out:

One Book, three copies. After one loan, Clean Code has 2 of 3 copies free.

Here is the same idea in code:

A book title is not the same as a copy

class Book {
  constructor(readonly isbn: string, readonly title: string, public free: number) {}
}

const cleanCode = new Book("111", "Clean Code", 3);
cleanCode.free--; // one copy lent out
console.log(`${cleanCode.title}: ${cleanCode.free} of 3 copies free`);

Expected output:

Clean Code: 2 of 3 copies free

The Loan

A loan is the record of one member having one copy. It remembers the due day, so it can work out the fine.

Step through asha's loan. She borrows on day 0, so it is due on day 14. The fine is 10 a day:

Here is the same idea in code:

A loan remembers who, what and when it is due

class Loan {
  constructor(readonly member: string, readonly isbn: string, readonly due: number) {}
  fine(returnDay: number, perDay: number): number {
    return Math.max(0, returnDay - this.due) * perDay;
  }
}

const loan = new Loan("asha", "111", 0 + 14);
console.log("on time:", loan.fine(14, 10));
console.log("3 days late:", loan.fine(17, 10));

Expected output:

on time: 0
3 days late: 30

The Rules, in Order

Write the borrowing rules as plain checks, in a fixed order, each with a clear reason.

Step through four borrow requests. The checks are: the 3-book limit, a free copy, and whether someone else is first in the queue:

MemberBooks heldFree copiesFirst in queueAnswer
asha12noneok
asha32nonerefused: already has 3 books
meena01ravirefused: reserved for ravi
ravi01raviok

Here is the same idea in code:

The rules, checked in a clear order

const MAX_LOANS = 3;

function canBorrow(loansHeld: number, freeCopies: number, firstInQueue: string | null, member: string): string {
  if (loansHeld >= MAX_LOANS) return "refused: already has 3 books";
  if (freeCopies === 0) return "refused: no copy free";
  if (firstInQueue !== null && firstInQueue !== member) return `refused: reserved for ${firstInQueue}`;
  return "ok";
}

console.log(canBorrow(1, 2, null, "asha"));
console.log(canBorrow(3, 2, null, "asha"));
console.log(canBorrow(0, 1, "ravi", "meena"));
console.log(canBorrow(0, 1, "ravi", "ravi"));

Expected output:

ok
refused: already has 3 books
refused: reserved for ravi
ok

Easy way to remember the design: a Book knows how many copies are free. A Loan knows who has a copy and when it is due. The Library checks the rules and keeps the waiting lists.

The Waiting List

When no copy is free, a member can reserve the book. The waiting list is a queue: the first person to reserve gets the next free copy.

Step through three reservations and one returned copy:

Here is the same idea in code:

A waiting list is a queue: first come, first served

const waiting: string[] = [];
waiting.push("ravi");
waiting.push("meena");
waiting.push("kiran");

console.log("next in line:", waiting[0]);
waiting.shift(); // ravi borrows the book
console.log("now waiting:", waiting.join(", "));

Expected output:

next in line: ravi
now waiting: meena, kiran

A reserved copy must be held for the first person in the queue. If anyone could borrow it when it comes back, the person who waited longest might never get it.

Room to Grow

Teachers may get more books for longer. Instead of if (member.type === "teacher") in many places, keep the rules in one table, or one strategy per member type.

The rules table has one row per member type:

Member typeMax booksLoan days
student314
teacher1030

The Library reads the row for the member, so a new member type is one new row.

Here is the code:

Different members, different rules: a strategy

interface MemberRules {
  maxLoans: number;
  loanDays: number;
}
const RULES: Record<string, MemberRules> = {
  student: { maxLoans: 3, loanDays: 14 },
  teacher: { maxLoans: 10, loanDays: 30 },
};

for (const kind of ["student", "teacher"]) {
  const r = RULES[kind];
  console.log(`${kind}: up to ${r.maxLoans} books for ${r.loanDays} days`);
}

Expected output:

student: up to 3 books for 14 days
teacher: up to 10 books for 30 days

Lab: Build It

Build the Library class. The hidden tests lend and return books, check the limit and fines, and check that the waiting list is fair.

Predict the Output

Common Mistakes

MistakeWrongCorrectWhy it happens
Title as one itemOnly one person can borrow Clean Code.A count of free copies.It is easy to forget a library has copies.
Due day not storedWorking out due days from the borrow date everywhere.Store due on the loan.The rule "14 days" seems simple enough to repeat.
Unfair queueA returned copy goes to whoever calls borrow first.Hold it for the first person waiting.The happy-path test has no queue.
Fine from the borrow dateCharging from day 0 instead of the due day.Count only days after the due day.Mixing up the two dates.
Vague errors"cannot borrow" for every reason.A clear reason for each rule.One error message is quicker to write.

Practice

What to Remember

  • A book has a count of free copies.
  • A loan records who, what and the due day.
  • Check the rules in a fixed order, each with a clear reason.
  • The waiting list is a queue: first come, first served.
  • Keep member rules in a table or strategy.

Further Reading

These are the sources used to check this case study:

Next: Vending Machine. You will use the State pattern to build a machine whose buttons do different things at different times.