# System Design — Designing a Library Management System

Designing a comprehensive **Library Management System (LMS)** involves deep attention to system architecture, database design, concurrency control, and fault tolerance.

This post explores the **core components and their underlying mechanisms**, via direct representation of SQL operations, as a beginner friendly system design walk through.

### 1\. Inventory Management

#### A. Book Identification and Cataloging

**Goals:**

*   Ensure each book copy is uniquely identifiable
*   Allow precise tracking of both the title (logical entity) and each physical copy (physical asset)
*   Support location-based queries and updates

**Logical vs Physical Book Modeling:**

*   Use a normalized model that separates:
*   **Book title metadata** (ISBN, title, author, genre, publication date)
*   **Physical copies** (book copies with barcodes or tags)

**Schema Design:**

*   `book_titles`: Logical metadata about a title
*   `book_copies`: Physical instances of the book

CREATE TABLE book\_titles (  
  book\_id UUID PRIMARY KEY,  
  isbn VARCHAR(13) UNIQUE,  
  title TEXT,  
  author TEXT,  
  genre TEXT,  
  publication\_year INTEGER  
);

CREATE TABLE book\_copies (  
  copy\_id UUID PRIMARY KEY,  
  book\_id UUID REFERENCES book\_titles(book\_id),  
  status ENUM('available', 'checked\_out', 'reserved', 'lost', 'damaged'),  
  location\_id UUID,  
  shelf\_code TEXT,  
  acquisition\_date DATE,  
  condition TEXT  
)

**Benefits:**

*   Clean separation allows for metadata updates without affecting copy states
*   Enables accurate inventory counts and distribution per title

#### B. Physical Location Tracking

**Goals:**

*   Provide deterministic answers to “Where is a book right now?”
*   Support user navigation and librarian retrieval

**Library Layout Modeling:**

*   Abstract the layout into hierarchical location units:
*   Building → Floor → Section → Shelf → Row
*   Model as a relational tree or as path-like strings (`/main/1st_floor/A3/Shelf2`)

**Schema**

CREATE TABLE locations (  
  location\_id UUID PRIMARY KEY,  
  parent\_id UUID,  
  name TEXT,  
  type ENUM('building', 'floor', 'section', 'shelf', 'bin'),  
  code TEXT  
);

*   Each `book_copies` row references a `location_id`
*   Hierarchical queries can be enabled using recursive SQL or precomputed paths

**Optimizations:**

*   Cache resolved paths (`full_location_path`) for faster reads
*   Use geospatial indexing if you integrate with physical map systems or wayfinding tools

#### C. Real-Time Availability and Status Transitions

**Status Model:**

Every book copy maintains a lifecycle state, typically:

*   `available` → default state, on shelf and borrowable
*   `checked_out` → currently loaned
*   `reserved` → on hold for a user
*   `in_transit` → being moved between branches
*   `lost` or `damaged` → temporarily or permanently removed

**State Transitions:**

*   Must be atomic and verified against constraints (e.g., can’t check out a reserved book)
*   Transitions are triggered by events such as borrow, return, reservation expiration, or manual updates

**Example State Transition Logic (Pseudocode):**

def checkout\_book(copy\_id, user\_id):  
    book = db.select\_for\_update('book\_copies', copy\_id)  
    if book.status != 'available':  
        raise Exception("Book not available")

db.update('book\_copies', copy\_id, status\='checked\_out')  
db.insert('transactions', {...})

**Triggers and Validation**

*   Use DB-level `CHECK` constraints or triggers to validate allowed transitions
*   Maintain a transition log table if historical status tracking is required

#### D. Scaling Inventory with Library Growth

**Challenges:**

*   As inventory grows, full scans and queries can degrade in performance
*   Libraries may need to support hundreds of thousands of titles and millions of transactions

**Scalability Mechanisms:**

**Indexing**:

*   Index common query fields like `status`, `location_id`, and `book_id`
*   Use composite indexes for multi-condition filters

**Partitioning**:

*   Horizontally partition `book_copies` by branch location or acquisition year
*   Helps with data locality and bulk operations

**Materialized Views**:

*   Precompute inventory summaries (e.g., count of available books per title)
*   Refresh periodically or incrementally

**Asynchronous Batch Jobs**:

*   Offload analytics and reporting (e.g., most borrowed books) to background jobs
*   Use ETL pipelines to feed warehouse databases

### 2\. User Management

### Authentication and Authorization

Security models must enforce **least privilege access** while ensuring usability.

*   **MFA Implementation**: Combine something the user knows (password) and something they have (email/phone OTP). Tokens can be time-based (TOTP) using libraries like `pyotp`.
*   **RBAC Schema**:
*   `CREATE TABLE user_roles ( user_id UUID, role ENUM('student', 'staff', 'admin'), PRIMARY KEY (user_id) );`
*   Authorization checks can be enforced through middleware in API gateways or service layers.
*   **Session Tokens**: Use **JWTs** (JSON Web Tokens) signed with HMAC-SHA256 for stateless authentication. JWT payload can include `role`, `user_id`, and `exp`.

### User Profiles

Storing and managing user preferences should be optimized for **fast retrieval and analytics**.

*   **Normalized Schema**: Keep high-volume interaction data (like borrowing history) in a separate table to avoid bloating the `users` table.
*   `CREATE TABLE borrow_history ( user_id UUID, book_id UUID, borrowed_at TIMESTAMP, returned_at TIMESTAMP );`
*   **Recommendation Engine Inputs**: For collaborative filtering, compute `user-user` or `item-item` similarity using **cosine similarity or Pearson correlation**, either precomputed and cached, or dynamically fetched.

### Fine Management

Fines introduce **monetary logic** requiring precision and traceability.

*   **Deterministic Fine Logic**: Implement a deterministic function:

def calculate\_fine(days\_overdue: int, rate: float = 0.50) -> float:       
    return max(0, days\_overdue \* rate)

*   Store only the overdue timestamp; derive the fine on read to reduce inconsistencies.
*   **Audit Logging**: Store fine-related actions in an immutable ledger-style log:
*   `CREATE TABLE fine_audit_log ( user_id UUID, amount DECIMAL(6,2), action ENUM('assessed', 'paid', 'waived'), timestamp TIMESTAMP );`

### 3\. Search and Discovery

### Search Functionality

Searching large datasets requires optimized indexing and inverted search structures.

*   **Inverted Index**: If not using external search engines, implement basic inverted indexes manually:
*   `CREATE TABLE word_index ( word TEXT, book_id UUID );`
*   This supports partial keyword matches but adds complexity for stemming/tokenization.
*   **Faceted Search**: Use composite indexes on (genre, author, year) to support filters:
*   `CREATE INDEX idx_facet ON books(genre, author, publication_year);`
*   **Caching Frequent Queries**: Store results of high-frequency queries in Redis with an LRU eviction policy. Use query fingerprints (e.g., hashed SQL strings) as keys.

### Recommendation System

**Collaborative Filtering Pipeline**:

*   Preprocess interaction matrix (`user_id × book_id`).
*   Use matrix factorization (e.g., SVD) to decompose into latent vectors.
*   Store precomputed vectors in a vector store (e.g., using Faiss or a Postgres extension like pgvector).

**Content-Based Filtering**:

*   Extract features from book metadata (e.g., genre, author, tags).
*   Build a TF-IDF matrix or use word embeddings for similarity calculations.

### 4\. Transaction Management

Transaction management in a library management system requires atomic, consistent handling of operations that affect multiple entities, including books, users, and system state. The operations must guarantee correctness under concurrent access, enforce business rules (e.g., borrow limits, reservation windows), and ensure recoverability in case of partial failures.

#### A. Borrowing and Returning Books

**Core Concepts:**

*   A borrow or return operation modifies multiple data entities:
*   The book’s status (e.g., from ‘available’ to ‘checked\_out’)
*   The transaction history
*   The user’s borrowing quota
*   The book’s current holder reference
*   The borrow history log

**Transactional Integrity:**

*   Use ACID-compliant database transactions to wrap all the related changes.
*   Leverage row-level locking (`SELECT ... FOR UPDATE`) to prevent concurrent modifications on the same book record.
*   Ensure that rollback is triggered on any failure to maintain atomicity.

**Example SQL Transaction Flow (Borrow):**

1.  Begin transaction.
2.  Fetch the book row and lock it using `FOR UPDATE`.
3.  Validate book availability and user quota.
4.  Update the book status to `checked_out`.
5.  Insert a new row into the `transactions` table with type = 'borrow'.
6.  Insert or update the `borrow_history` table.
7.  Update the user’s active borrow count.
8.  Commit the transaction.

**Return Operation:**

*   Similar steps apply with:
*   Book status updated to `available`
*   `return_date` set in `borrow_history`
*   Fine calculated and logged if overdue

#### B. Reservation System

**Deferred Transactions:**

*   Reservations do not immediately modify the book state but add an entry to a reservation queue.
*   Upon book return, a background worker or trigger checks the reservation queue and promotes the next eligible user.

**Queue Design:**

*   Implement as a FIFO queue with tie-breaking on reservation priority or timestamp.
*   Each entry includes book\_id, user\_id, status (waiting, notified, expired, fulfilled), and timestamp.

**Reservation Lifecycle:**

*   Requested → Notified → Expired or Fulfilled
*   Upon book availability:
*   Change book status to `reserved`
*   Update reservation entry to `notified`
*   Start a hold window timer (e.g., 48 hours)
*   If the user does not claim within the hold window, mark the reservation as `expired` and move to the next in line

**Concurrency Considerations:**

*   Reservation handling should be serialized or protected with advisory locks to avoid promoting multiple users simultaneously.

#### C. Renewal Handling

**Renewal Logic:**

*   A renewal request is an update to an active borrow record, typically extending the due date.
*   Validations must check:
*   If the book is reserved by another user (block renewal)
*   If the user has exceeded the allowed number of renewals
*   If the book is already overdue (may require fine payment before renewal)

**Data Updates:**

*   Modify the `due_date` in `borrow_history`
*   Append a ‘renew’ type entry in the `transactions` table
*   Log the renewal action for audit purposes

**Idempotency:**

*   Ensure the same renewal request (e.g., due to UI retries) does not lead to duplicate extensions. Use a combination of `user_id`, `book_id`, and timestamp to detect repeat requests.

#### D. Fine Calculation

**Design Principles:**

*   Fines should be calculated deterministically based on borrow/return timestamps and library policy.
*   Do not store calculated fines directly. Store only:
*   Borrow date
*   Due date
*   Return date
*   Fine policy (per-day rate)

**Calculation Logic:**

*   On return:
*   If return date > due date, calculate days late
*   Multiply by fine rate (e.g., $0.50/day)
*   For user account view:
*   Run the same computation live or cache it periodically for performance

**Fine Transaction Logs:**

*   Store fine-related actions in a dedicated table with fields:
*   User ID
*   Book ID
*   Amount
*   Action (assessed, paid, waived)
*   Timestamp
*   Do not overwrite fine entries; use immutable logs to maintain auditability

#### E. Concurrency Control

**Key Conflict Scenarios:**

*   Two users trying to borrow the same book simultaneously
*   A user trying to renew a book that is already reserved by someone else
*   An admin disabling a book record while it’s being borrowed

**Strategies:**

*   Use pessimistic locking (`SELECT ... FOR UPDATE`) on critical resources such as book records
*   Implement optimistic locking with a version counter to detect write conflicts
*   Always access shared resources in a consistent order to prevent deadlocks
*   Use application-level mutexes or advisory locks for workflows involving multiple entities (e.g., checking book, user, and reservation state)

**Retry Logic:**

*   If an operation fails due to a concurrent modification, return a 409 Conflict or equivalent
*   Let the client decide whether to reattempt the operation with updated data

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1752429788808/23ecb38e-ffc6-479a-a32c-eb9835a79d92.png)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1752429790702/970d532e-271c-4788-9992-de2161fa22b0.png)

Full Answer: [https://bugfree.ai/practice/system-design/voting-system/solutions/BbJ9EbA2sXL2C4xc](https://bugfree.ai/practice/system-design/voting-system/solutions/BbJ9EbA2sXL2C4xc)
