Loading...

12-Hour Money-Back Guarantee

Design (LLD) Online Book Management System - Machine Coding Interview

Design (LLD) Online Book Management System - Machine Coding Interview

Design (LLD) Online Book Management System - Machine Coding Interview

16 Dec 20246 min read

Asked in LLD Interview

Functional Requirements:

  1. Search for a Book:

    • Users should be able to search for books in the system based on various criteria, such as title, author, or genre.
  2. Read a Book:

    • A user should be able to select a book from the search results and start reading it online.
  3. User Registration and Management:

    • The system should support user registration.

    • Registered users should be able to extend their access or subscription.

  4. Concurrency Constraints:

    • Only one active user can interact with the system at a time.

    • Each user can have only one active book assigned for reading at any given time.

Non-Functional Requirements:

  1. The system should be scalable to handle multiple users and a large catalog of books in the future.

  2. It should ensure data consistency and proper state management when managing active users and books.

  3. The design should incorporate thread safety for concurrent user actions.

Solution

Focus of this solution is on 4th Requirement.

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

// Enum for Book Status
enum BOOK_STATUS {
    FREE, ISSUED
}

// Abstract class for Person
abstract class Person {
    String personId;

    public Person(String personId) {
        this.personId = personId;
    }

    public String getPersonId() {
        return personId;
    }
}

// User Class
class User extends Person {
    private Book currentIssuedBook;

    public User(String personId) {
        super(personId);
    }

    public Book getCurrentIssuedBook() {
        return currentIssuedBook;
    }

    public void setCurrentIssuedBook(Book book) {
        this.currentIssuedBook = book;
    }
}

// Abstract class for Book
abstract class Book {
    String bookId;
    BOOK_STATUS status;

    public Book(String bookId) {
        this.bookId = bookId;
        this.status = BOOK_STATUS.FREE;
    }

    public String getBookId() {
        return bookId;
    }

    public BOOK_STATUS getStatus() {
        return status;
    }

    public void setStatus(BOOK_STATUS status) {
        this.status = status;
    }
}

// Concrete Classes for Books
class ComicBook extends Book {
    public ComicBook(String bookId) {
        super(bookId);
    }
}

class OthersBook extends Book {
    public OthersBook(String bookId) {
        super(bookId);
    }
}

// Factory for creating Users
class UserFactory {
    public static User createUser(String personId) {
        return new User(personId);
    }
}

// Factory for creating Books
class BookFactory {
    public static Book createBook(String bookType, String bookId) {
        switch (bookType) {
            case "Comic":
                return new ComicBook(bookId);
            default:
                return new OthersBook(bookId);
        }
    }
}

// Interface for Book Management
interface BookManage {
    boolean issueBook(Book book, User user);
    boolean returnBook(User user);
    List<Book> searchBooks(String query);
}

// Book Management System Implementation
class BookManagementSystem implements BookManage {
    private final Map<String, Book> bookDatabase = new HashMap<>();
    private final AtomicReference<User> activeUser = new AtomicReference<>(null);

    public BookManagementSystem() {
        // Initialize books in the database
        bookDatabase.put("B001", BookFactory.createBook("Comic", "B001"));
        bookDatabase.put("B002", BookFactory.createBook("Others", "B002"));
        bookDatabase.put("B003", BookFactory.createBook("Comic", "B003"));
    }

    // Issue a book to a user
    @Override
    public synchronized boolean issueBook(Book book, User user) {
        if (!setActiveUser(user)) {
            System.out.println("Only one active user is allowed at a time.");
            return false;
        }
        if (user.getCurrentIssuedBook() != null) {
            System.out.println("User already has an issued book.");
            return false;
        }
        if (book.getStatus() == BOOK_STATUS.ISSUED) {
            System.out.println("Book is already issued.");
            return false;
        }
        book.setStatus(BOOK_STATUS.ISSUED);
        user.setCurrentIssuedBook(book);
        System.out.println("Book " + book.getBookId() + " issued to user " + user.getPersonId());
        return true;
    }

    // Return a book
    @Override
    public synchronized boolean returnBook(User user) {
        if (!isActiveUser(user)) {
            System.out.println("Only the active user can return a book.");
            return false;
        }
        Book book = user.getCurrentIssuedBook();
        if (book == null) {
            System.out.println("User has no issued book to return.");
            return false;
        }
        book.setStatus(BOOK_STATUS.FREE);
        user.setCurrentIssuedBook(null);
        clearActiveUser();
        System.out.println("Book " + book.getBookId() + " returned by user " + user.getPersonId());
        return true;
    }

    // Search for books (simple title contains query search)
    @Override
    public List<Book> searchBooks(String query) {
        List<Book> result = new ArrayList<>();
        for (Book book : bookDatabase.values()) {
            if (book.getBookId().contains(query)) {
                result.add(book);
            }
        }
        return result;
    }

    // Set an active user
    private boolean setActiveUser(User user) {
        return activeUser.compareAndSet(null, user);
    }

    // Clear the active user
    private void clearActiveUser() {
        activeUser.set(null);
    }

    // Check if the given user is the active user
    private boolean isActiveUser(User user) {
        return activeUser.get() == user;
    }
}

// Main Class
public class Solution {
    public static void main(String[] args) {
        BookManagementSystem system = new BookManagementSystem();

        User user1 = UserFactory.createUser("U001");
        User user2 = UserFactory.createUser("U002");

        Book book1 = system.searchBooks("B001").get(0);

        // Test Case 1: Issue book to user1
        system.issueBook(book1, user1);

        // Test Case 2: Try issuing another book to user1
        Book book2 = system.searchBooks("B002").get(0);
        system.issueBook(book2, user1);

        // Test Case 3: Try issuing a book to user2 while user1 is active
        system.issueBook(book2, user2);

        // Test Case 4: Return book by user1
        system.returnBook(user1);

        // Test Case 5: Issue book to user2 after user1 returns their book
        system.issueBook(book2, user2);
    }
}