Library Management System

This I have written as part of my LLD Practice. Please let me know further imporvements that can be done.

Also tell current design is Strong/Average/Lean Hire ?

Other Design Problems

/*
Similiar to car rental System

Flow->
Domain ===> Repository(DB) ==> Service (Buisness Logic) ==> Controller (Client Code)

======= Domain =======
Book
Search
PricingStrategy
FineStrategy
PaymentModule
Booking

======= Repository (DB Logic) =======
LibraryRepository
BookingRepository

======= Service (Buisness Logic) =======
SearchService
LibraryRentalService

======= Controller (Client Interaction) =======

LibraryRentalController (Client Interaction)

Client Code

*/
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

enum BookGenre{
    Literature, Science, Fiction
}

enum BookStatus{
    Available, Reserved
}

enum PaymentMode{
    UPI, Cash
}

class User{
    String id;
    String name;
    String email;
    String phoneNo;
}

class Book{
    String bookId;
    String bookName;
    String author;
    BookStatus bookStatus;
    BookGenre bookGenre;
}

interface PricingStrategy{
    double calculatePrice(List<Book> books, LocalDateTime from, LocalDateTime to);
}

class FlatPricingStrategy implements PricingStrategy{
    @Override
    public double calculatePrice(List<Book> books, LocalDateTime from, LocalDateTime to){
//        long days= ChronoUnit.DAYS.between(from,to);
        long days= Duration.between(from,to).toDays();
        return 100*days;
    }
}

interface FineStrategy{
    double calculateFine(LocalDateTime actualReturn, LocalDate currentDate);
}

class FlatFineStrategy implements FineStrategy{
    @Override
    public double calculateFine(LocalDateTime actualReturn, LocalDate currentDate){
        long days= Duration.between(actualReturn,currentDate).toDays();
        if(days<0) return 0.0;
        return 100*days;
    }
}

interface Payment{
    void pay(double amount);
}

class UPIPayment implements Payment{
    @Override
    void pay(double amount){

    }
}

class CashPayment implements Payment{
    @Override
    void pay(double amount){

    }
}

class PaymentFactory{
    static public Payment getPaymentMethod(PaymentMode mode){
        switch(mode){
            case UPI: return new UPIPayment();
            case Cash: return new CashPayment();
            default: throw new IllegalArgumentException("Invalid Method");
        }
    }
}

class Booking{
    String id;
    User user;
    List<Book> books;
    LocalDateTime from;
    LocalDateTime to;
    double amount;
    PaymentMode paymentMode;
    boolean isActive;

    Booking(User user, List<Book> books, LocalDateTime from, LocalDateTime to, PaymentMode mode){
        this.id= UUID.randomUUID().toString();
        this.books=books;
        this.from=from;
        this.to=to;
        this.paymentMode=mode;
        this.isActive=true;
    }
}

/* ====== Repository ====== */

class BookRepository{
    Map<String,Book> booksDb=new HashMap<>();

    synchronized boolean addBook(Book book){
        booksDb.put(book.bookId, book);
        return true;
    }

    List<Book> getAllBooks(){
        return new ArrayList<>(booksDb.values());
    }

    synchronized boolean isAvailable(String bookId){
        return booksDb.get(bookId).bookStatus==BookStatus.Available;
    }

    synchronized boolean reserveBooks(List<Book> books){
        for(Book book:books){
            Book bookToReserve=booksDb.get(book.bookId);
            bookToReserve.bookStatus=BookStatus.Reserved;
            booksDb.put(bookToReserve.bookId, bookToReserve);
        }
        return true;
    }

    synchronized boolean markAvailableBooks(List<Book> books){
        for(Book book:books){
            Book bookToReserve=booksDb.get(book.bookId);
            bookToReserve.bookStatus=BookStatus.Available;
            booksDb.put(bookToReserve.bookId, bookToReserve);
        }
        return true;
    }
}


class BookingRepository{
    Map<String,Booking> bookingDb;

    boolean addBooking(Booking booking){
        bookingDb.put(booking.id, booking);
        return true;
    }

    Booking getBooking(String bookingId){
        return bookingDb.get(bookingId);
    }

    boolean removeBooking(String bookingId){
        bookingDb.remove(bookingId);
        return true;
    }
}

/* ====== Service ====== */

class SearchService{
    BookRepository bookRepository;

    List<Book> searchByName(String name){
        List<Book> list=new ArrayList<>();
        for(Book book:bookRepository.getAllBooks()){
            if(book.bookName.equals(name)){
                list.add(book);
            }
        }
        return list;
    }

    List<Book> searchByAuthor(String author){
        List<Book> list=new ArrayList<>();
        for(Book book:bookRepository.getAllBooks()){
            if(book.author.equals(author)){
                list.add(book);
            }
        }
        return list;
    }

    List<Book> searchByBookGenre(BookGenre bookGenre){
        List<Book> list=new ArrayList<>();
        for(Book book:bookRepository.getAllBooks()){
            if(book.bookGenre==bookGenre){
                list.add(book);
            }
        }
        return list;
    }
}

class LibraryService{
    BookRepository bookRepository;
    BookingRepository bookingRepository;
    PricingStrategy pricingStrategy;
    FineStrategy fineStrategy;

    synchronized Booking issueBook(User user, List<Book> books, LocalDateTime from, LocalDateTime to, PaymentMode paymentMode){
        List<Book> toIssue=new ArrayList<>();
        for(Book book:books){
            if(book.bookStatus==BookStatus.Available) toIssue.add(book);
        }

        bookRepository.reserveBooks(toIssue);
        double amount=pricingStrategy.calculatePrice(toIssue, from, to);
        Payment payment=PaymentFactory.getPaymentMethod(paymentMode);
        payment.pay(amount);
        Booking booking=new Booking(user,toIssue,from,to,paymentMode);
        bookingRepository.addBooking(booking);
        return booking;
    }

    synchronized boolean cancelBooking(String bookingId){
        Booking booking=bookingRepository.getBooking(bookingId);
        if(booking==null){
            throw new RuntimeException("Invalid id");
        }
        bookRepository.markAvailableBooks(booking.books);
        bookingRepository.removeBooking(bookingId);
        booking.isActive=false;
        return true;
    }

    synchronized boolean returnBook(String bookingId){
        Booking booking=bookingRepository.getBooking(bookingId);
        if(booking==null){
            throw new RuntimeException("Invalid id");
            return false;
        }
        double fine=fineStrategy.calculateFine(booking.to,LocalDateTime.now());
        if(fine>0){
            Payment payment=PaymentFactory.getPaymentMethod(booking.paymentMode); //payment mode??????
            payment.pay(fine);
        }
        bookRepository.markAvailableBooks(booking.books);
        booking.isActive=false;
        return true;
    }
}


/* ====== Controller ====== */

class LibraryController{
    LibraryService libraryService;
    SearchService searchService;

    synchronized Booking issueBook(User user, List<Book> books, LocalDateTime from, LocalDateTime to, PaymentMode paymentMode){
        return libraryService.issueBook(user, books, from, to, paymentMode);
    }

    synchronized boolean cancelBooking(String bookingId){
        return libraryService.cancelBooking(bookingId);
    }

    synchronized boolean returnBook(String bookingId){
        return libraryService.returnBook(bookingId);
    }

    List<Book> searchByName(String name){
        return searchService.searchByName(name);
    }

    List<Book> searchByAuthor(String author){
        return searchService.searchByAuthor(author);
    }

    List<Book> searchByBookGenre(BookGenre bookGenre){
        return searchService.searchByBookGenre(bookGenre);
    }

}


public class Library {
    public static void main(String[] args) {
        User user=new User();
        LibraryController controller=new LibraryController();

        List<Book> books=controller.searchByAuthor("RD Sharma");
        Booking booking=controller.issueBook(user,new ArrayList<>((Collection) books.get(0)),LocalDateTime.now(),LocalDateTime.of(2025,9,12,13,30),PaymentMode.Cash);
        controller.returnBook(booking.id);
    }
}
Comments (3)