problemmediumood

OOD - Library Management System

MediumUpdated: Jan 1, 2026

Problem Statement

Design a comprehensive library management system that efficiently handles all aspects of library operations including book catalog management, member management, lending operations, reservations, fine collection, and notifications.

The system must support:

  • Catalog Management: Add, update, remove books with multiple copies (book items). Search by title, author, ISBN, subject, publication date with advanced filtering and sorting
  • Membership System: Multiple membership tiers (Basic, Premium, Gold) with different borrowing privileges, limits, and fee structures
  • Lending Operations: Check out, return, and renew book items with proper validation of borrowing limits, due dates, and reservation queues
  • Reservation System: Allow members to reserve currently unavailable books with FIFO queue management and automatic notifications when books become available
  • Fine Management: Calculate and collect fines for overdue books with configurable rates, grace periods, and membership-specific policies
  • Notification System: Multi-channel notifications (email, SMS, push) for events like due date reminders, reservation availability, overdue warnings, and hold expirations
  • Administrative Functions: Librarian operations for member management, catalog updates, fine waivers, and reporting (popular books, overdue items, revenue)
  • Book Item Tracking: Track individual copies with barcode, condition (new, good, worn, damaged), location (rack, shelf), and status (available, checked out, reserved, lost, under repair)
  • Special Collections: Support for reference-only books, rare books, and media types (audiobooks, ebooks, DVDs, magazines)

Requirements Analysis

Functional Requirements

Core Operations:

  1. Catalog Management

    • Add new books with metadata (ISBN, title, author, publisher, subject, publication date)
    • Add multiple physical copies (book items) with unique barcodes
    • Update book information and copy status
    • Remove books and track removed copies
    • Tag books (fiction, non-fiction, bestseller, award-winner)
  2. Search and Discovery

    • Search by title, author, ISBN, subject, tags with fuzzy matching
    • Filter by format (hardcover, paperback, ebook, audiobook)
    • Filter by availability status
    • Sort by relevance, publication date, popularity
    • Advanced search combining multiple criteria
  3. Member Management

    • Register new members with membership tiers
    • Upgrade/downgrade memberships
    • Track borrowing history and preferences
    • Block members for violations (excessive fines, lost books)
    • Set borrowing limits based on membership level
  4. Checkout Operations

    • Validate member eligibility (active account, not exceeded limit, no excessive fines)
    • Check book availability (not reference-only, not already checked out)
    • Calculate due date based on book type and membership
    • Generate checkout receipt
    • Update book item status and member's checked-out count
  5. Return Operations

    • Scan returned book item
    • Calculate fines if overdue
    • Update book status to available
    • Check reservation queue and notify next member
    • Generate return receipt
  6. Renewal Operations

    • Allow renewal if no reservations pending
    • Limit renewals (max 2 times)
    • Extend due date
    • Prevent renewal for overdue items with excessive fines
  7. Reservation System

    • Place reservation for currently unavailable books
    • Maintain FIFO queue for multiple reservations
    • Notify member when reserved book available
    • Hold book for 48 hours after notification
    • Auto-cancel reservation if not picked up
    • Allow reservation cancellation
  8. Fine Calculation

    • Daily fine rate varies by membership tier
    • Grace period (1-3 days depending on membership)
    • Maximum fine cap per book
    • Waive fines for special circumstances
    • Track fine payment history
  9. Notifications

    • Due date reminder (3 days before, 1 day before)
    • Overdue notice (1 day after, 7 days after)
    • Reservation available notification
    • Reservation expiring notification (24 hours before hold expiry)
    • Account suspension notice

Non-Functional Requirements

  1. Performance: Catalog search < 500ms for 100K+ books, checkout/return < 2 seconds
  2. Scalability: Support 10K+ members, 100K+ books, 1K+ concurrent operations
  3. Reliability: Zero data loss for transactions, consistent state across distributed operations
  4. Availability: 99.9% uptime, graceful degradation if notification service fails
  5. Security: Secure member authentication, role-based access (member vs librarian), audit logging
  6. Usability: Intuitive search interface, clear error messages, accessibility compliance

Use Case Diagram

graph TB
    subgraph Actors
        Member[๐Ÿ‘ค Member]
        Librarian[๐Ÿ‘” Librarian]
        System[๐Ÿ–ฅ๏ธ System]
    end

    subgraph "Member Use Cases"
        UC1[๐Ÿ” Search Catalog]
        UC2[๐Ÿ“– Checkout Book]
        UC3[๐Ÿ“š Return Book]
        UC4[๐Ÿ”„ Renew Book]
        UC5[๐Ÿ“… Reserve Book]
        UC6[โŒ Cancel Reservation]
        UC7[๐Ÿ’ณ Pay Fine]
        UC8[๐Ÿ“œ View Borrowing History]
        UC9[๐Ÿ“Š View Account Status]
    end

    subgraph "Librarian Use Cases"
        UC10[โž• Add Book to Catalog]
        UC11[โœ๏ธ Update Book Information]
        UC12[๐Ÿ—‘๏ธ Remove Book]
        UC13[๐Ÿ‘ฅ Register Member]
        UC14[๐Ÿ”ง Manage Member Account]
        UC15[๐Ÿ’ฐ Waive Fine]
        UC16[๐Ÿ“Š Generate Reports]
        UC17[๐Ÿท๏ธ Mark Book Lost/Damaged]
        UC18[โœ… Issue Replacement Card]
    end

    subgraph "System Use Cases"
        UC19[๐Ÿ”” Send Notifications]
        UC20[โš ๏ธ Calculate Fines]
        UC21[๐Ÿ“ข Process Reservation Queue]
        UC22[๐Ÿšซ Auto-Cancel Expired Holds]
    end

    Member --> UC1
    Member --> UC2
    Member --> UC3
    Member --> UC4
    Member --> UC5
    Member --> UC6
    Member --> UC7
    Member --> UC8
    Member --> UC9

    Librarian --> UC10
    Librarian --> UC11
    Librarian --> UC12
    Librarian --> UC13
    Librarian --> UC14
    Librarian --> UC15
    Librarian --> UC16
    Librarian --> UC17
    Librarian --> UC18
    Librarian -.-> UC1

    System --> UC19
    System --> UC20
    System --> UC21
    System --> UC22

    style UC1 fill:#e1f5ff,stroke:#01579b,stroke-width:2px
    style UC2 fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
    style UC3 fill:#fff9c4,stroke:#f57f17,stroke-width:2px
    style UC4 fill:#f3e5f5,stroke:#6a1b9a,stroke-width:2px
    style UC5 fill:#ffe0b2,stroke:#e65100,stroke-width:2px
    style UC6 fill:#ffcdd2,stroke:#c62828,stroke-width:2px
    style UC7 fill:#b2dfdb,stroke:#00695c,stroke-width:2px
    style UC8 fill:#d1c4e9,stroke:#4527a0,stroke-width:2px
    style UC9 fill:#b2ebf2,stroke:#006064,stroke-width:2px
    style UC10 fill:#c5e1a5,stroke:#558b2f,stroke-width:2px
    style UC11 fill:#ffccbc,stroke:#bf360c,stroke-width:2px
    style UC12 fill:#ffab91,stroke:#d84315,stroke-width:2px
    style UC13 fill:#ce93d8,stroke:#6a1b9a,stroke-width:2px
    style UC14 fill:#ffe082,stroke:#f9a825,stroke-width:2px
    style UC15 fill:#a5d6a7,stroke:#388e3c,stroke-width:2px
    style UC16 fill:#90caf9,stroke:#1565c0,stroke-width:2px
    style UC17 fill:#ef9a9a,stroke:#c62828,stroke-width:2px
    style UC18 fill:#b39ddb,stroke:#5e35b1,stroke-width:2px
    style UC19 fill:#80deea,stroke:#00695c,stroke-width:2px
    style UC20 fill:#ffcc80,stroke:#ef6c00,stroke-width:2px
    style UC21 fill:#bcaaa4,stroke:#5d4037,stroke-width:2px
    style UC22 fill:#f48fb1,stroke:#ad1457,stroke-width:2px

Class Diagram

The design demonstrates multiple key patterns:

  • Strategy Pattern: FineCalculationStrategy for flexible fine policies
  • Observer Pattern: NotificationService observers for multi-channel notifications
  • State Pattern: BookItemState for book lifecycle management
  • Command Pattern: Library operations (CheckoutCommand, ReturnCommand, etc.)
  • Composite Pattern: SearchCriteria for complex catalog queries
classDiagram
    class Library {
        -String name
        -Address address
        -Catalog catalog
        -List~Member~ members
        -List~Librarian~ librarians
        -NotificationService notificationService
        -ReservationManager reservationManager
        +addBook(book)
        +removeBook(isbn)
        +registerMember(member)
        +getAvailableBooks() List~BookItem~
    }

    class Catalog {
        -Map~String,Book~ booksByISBN
        -Map~String,List~Book~~ booksByTitle
        -Map~String,List~Book~~ booksByAuthor
        -Map~String,List~Book~~ booksBySubject
        +addBook(book)
        +removeBook(isbn)
        +search(criteria) List~Book~
        +getBookByISBN(isbn) Book
    }

    class SearchCriteria {
        <<interface>>
        +matches(book) boolean
    }

    class TitleSearchCriteria {
        -String title
        +matches(book) boolean
    }

    class AuthorSearchCriteria {
        -String authorName
        +matches(book) boolean
    }

    class CompositeSearchCriteria {
        -List~SearchCriteria~ criteria
        -SearchOperator operator
        +matches(book) boolean
    }

    class Book {
        -String isbn
        -String title
        -List~Author~ authors
        -String publisher
        -String subject
        -LocalDate publicationDate
        -int numberOfPages
        -List~String~ tags
        -List~BookItem~ items
        +addBookItem(item)
        +getAvailableItems() List~BookItem~
        +getTotalCopies() int
    }

    class Author {
        -String name
        -String biography
        -List~Book~ books
    }

    class BookItem {
        -String barcode
        -Book book
        -BookFormat format
        -BookItemState state
        -BookCondition condition
        -double price
        -LocalDate purchasedDate
        -String rackLocation
        -boolean isReferenceOnly
        +isAvailable() boolean
        +checkout(member)
        +returnItem()
        +reserve(member)
        +setState(state)
    }

    class BookFormat {
        <<enumeration>>
        HARDCOVER
        PAPERBACK
        EBOOK
        AUDIOBOOK
        MAGAZINE
        DVD
    }

    class BookItemState {
        <<interface>>
        +checkout(item, member)
        +returnItem(item)
        +reserve(item, member)
    }

    class AvailableState {
        +checkout(item, member)
        +returnItem(item)
        +reserve(item, member)
    }

    class CheckedOutState {
        +checkout(item, member)
        +returnItem(item)
        +reserve(item, member)
    }

    class ReservedState {
        +checkout(item, member)
        +returnItem(item)
        +reserve(item, member)
    }

    class LostState {
        +checkout(item, member)
        +returnItem(item)
        +reserve(item, member)
    }

    class BookCondition {
        <<enumeration>>
        NEW
        GOOD
        FAIR
        WORN
        DAMAGED
    }

    class Account {
        <<abstract>>
        -String accountId
        -String username
        -String password
        -Person person
        -AccountStatus status
        +resetPassword()
        +getAccountStatus() AccountStatus
    }

    class Member {
        -String memberId
        -MembershipTier tier
        -LocalDate membershipDate
        -int totalBooksCheckedOut
        -double outstandingFines
        -List~BookLending~ activeLendings
        -List~Reservation~ activeReservations
        -BorrowingHistory history
        +canCheckout() boolean
        +getTotalFines() double
        +getBorrowingLimit() int
        +checkout(bookItem)
        +returnBook(bookItem)
        +renewBook(lending)
        +reserveBook(book)
    }

    class MembershipTier {
        <<enumeration>>
        BASIC
        PREMIUM
        GOLD
    }

    class Librarian {
        -String employeeId
        +addBookToCatalog(book)
        +removeBookFromCatalog(isbn)
        +registerMember(member)
        +blockMember(memberId)
        +waiveFine(memberId, amount)
        +generateReport(type) Report
    }

    class Person {
        -String name
        -String email
        -String phone
        -Address address
    }

    class AccountStatus {
        <<enumeration>>
        ACTIVE
        SUSPENDED
        CLOSED
        BLACKLISTED
    }

    class BookLending {
        -String lendingId
        -BookItem bookItem
        -Member member
        -LocalDateTime checkoutDate
        -LocalDateTime dueDate
        -LocalDateTime returnDate
        -int renewalCount
        -LendingStatus status
        +renew()
        +returnBook()
        +isOverdue() boolean
        +getDaysOverdue() int
        +calculateFine() Money
    }

    class LendingStatus {
        <<enumeration>>
        ACTIVE
        RETURNED
        OVERDUE
        LOST
    }

    class Reservation {
        -String reservationId
        -Book book
        -Member member
        -LocalDateTime createdDate
        -LocalDateTime notifiedDate
        -LocalDateTime expiryDate
        -ReservationStatus status
        -int queuePosition
        +cancel()
        +fulfill(bookItem)
        +isExpired() boolean
    }

    class ReservationStatus {
        <<enumeration>>
        PENDING
        NOTIFIED
        COMPLETED
        CANCELLED
        EXPIRED
    }

    class ReservationManager {
        -Map~String,Queue~Reservation~~ reservationQueues
        +addReservation(reservation)
        +cancelReservation(reservationId)
        +getNextReservation(book) Reservation
        +notifyMember(reservation)
        +processQueue(book)
    }

    class FineCalculationStrategy {
        <<interface>>
        +calculateFine(daysOverdue, tier) Money
    }

    class StandardFineStrategy {
        -Map~MembershipTier,Money~ dailyRates
        -Map~MembershipTier,Integer~ gracePeriods
        -Money maxFinePerBook
        +calculateFine(daysOverdue, tier) Money
    }

    class GracePeriodFineStrategy {
        -FineCalculationStrategy baseStrategy
        -int graceDays
        +calculateFine(daysOverdue, tier) Money
    }

    class Fine {
        -String fineId
        -Member member
        -BookLending lending
        -Money amount
        -LocalDateTime createdDate
        -LocalDateTime paidDate
        -FineStatus status
        +pay()
        +waive()
    }

    class FineStatus {
        <<enumeration>>
        PENDING
        PAID
        WAIVED
    }

    class NotificationService {
        -List~NotificationChannel~ channels
        +registerChannel(channel)
        +notify(member, notification)
        +sendDueDateReminder(lending)
        +sendOverdueNotice(lending)
        +sendReservationAvailable(reservation)
    }

    class NotificationChannel {
        <<interface>>
        +send(recipient, message)
    }

    class EmailNotification {
        -String smtpServer
        +send(recipient, message)
    }

    class SMSNotification {
        -String twilioApiKey
        +send(recipient, message)
    }

    class PushNotification {
        -String firebaseKey
        +send(recipient, message)
    }

    class Notification {
        -String notificationId
        -Member recipient
        -NotificationType type
        -String message
        -LocalDateTime sentDate
        -boolean isRead
    }

    class NotificationType {
        <<enumeration>>
        DUE_DATE_REMINDER
        OVERDUE_NOTICE
        RESERVATION_AVAILABLE
        RESERVATION_EXPIRING
        ACCOUNT_SUSPENDED
    }

    class LibraryCommand {
        <<interface>>
        +execute()
        +undo()
    }

    class CheckoutCommand {
        -Member member
        -BookItem bookItem
        -Library library
        +execute()
        +undo()
    }

    class ReturnCommand {
        -BookLending lending
        -Library library
        +execute()
        +undo()
    }

    class RenewCommand {
        -BookLending lending
        +execute()
        +undo()
    }

    Library "1" *-- "1" Catalog
    Library "1" *-- "many" Member
    Library "1" *-- "many" Librarian
    Library "1" --> "1" NotificationService
    Library "1" --> "1" ReservationManager

    Catalog "1" *-- "many" Book
    Catalog --> SearchCriteria

    SearchCriteria <|.. TitleSearchCriteria
    SearchCriteria <|.. AuthorSearchCriteria
    SearchCriteria <|.. CompositeSearchCriteria

    Book "1" *-- "many" BookItem
    Book "many" --> "many" Author

    BookItem --> BookFormat
    BookItem --> BookCondition
    BookItem --> BookItemState

    BookItemState <|.. AvailableState
    BookItemState <|.. CheckedOutState
    BookItemState <|.. ReservedState
    BookItemState <|.. LostState

    Account <|-- Member
    Account <|-- Librarian
    Account --> Person
    Account --> AccountStatus

    Member --> MembershipTier
    Member "1" *-- "many" BookLending
    Member "1" *-- "many" Reservation

    BookLending --> BookItem
    BookLending --> Member
    BookLending --> LendingStatus

    Reservation --> Book
    Reservation --> Member
    Reservation --> ReservationStatus

    ReservationManager "1" *-- "many" Reservation

    FineCalculationStrategy <|.. StandardFineStrategy
    FineCalculationStrategy <|.. GracePeriodFineStrategy

    Fine --> Member
    Fine --> BookLending
    Fine --> FineStatus

    NotificationService "1" --> "many" NotificationChannel

    NotificationChannel <|.. EmailNotification
    NotificationChannel <|.. SMSNotification
    NotificationChannel <|.. PushNotification

    Notification --> Member
    Notification --> NotificationType

    LibraryCommand <|.. CheckoutCommand
    LibraryCommand <|.. ReturnCommand
    LibraryCommand <|.. RenewCommand

Activity Diagrams

1. Book Checkout Flow

flowchart TD
    Start([๐Ÿ‘ค Member Searches Catalog]) --> Search[๐Ÿ” Search for Book<br/>by Title/Author/ISBN]

    Search --> SelectBook[๐Ÿ“– Select Desired Book]
    SelectBook --> CheckCopies{Available<br/>Copies?}

    CheckCopies -->|No| Reserve[๐Ÿ“… Place Reservation<br/>Join Wait Queue]
    Reserve --> NotifyQueue[๐Ÿ“ง Notify Member of<br/>Queue Position]
    NotifyQueue --> End1([โณ Waiting for Availability])

    CheckCopies -->|Yes| CheckReference{Reference<br/>Only?}
    CheckReference -->|Yes| DenyRef[โŒ Cannot Check Out<br/>Reference Books]
    DenyRef --> End2([๐Ÿšซ Checkout Denied])

    CheckReference -->|No| CheckAccount{Member<br/>Account Active?}
    CheckAccount -->|No| DenyAccount[โŒ Account Suspended<br/>Contact Librarian]
    DenyAccount --> End2

    CheckAccount -->|Yes| CheckFines{Outstanding<br/>Fines > $10?}
    CheckFines -->|Yes| PayFines[๐Ÿ’ฐ Must Pay Fines First]
    PayFines --> End2

    CheckFines -->|No| CheckLimit{Reached<br/>Borrowing Limit?}
    CheckLimit -->|Yes| DenyLimit[โŒ Maximum Books<br/>Already Checked Out]
    DenyLimit --> End2

    CheckLimit -->|No| CreateLending[โœ… Create BookLending Record<br/>Generate Lending ID]

    CreateLending --> CalcDueDate[๐Ÿ“… Calculate Due Date<br/>Based on Membership Tier]
    CalcDueDate --> UpdateItem[๐Ÿ”„ Update BookItem State<br/>to CHECKED_OUT]

    UpdateItem --> UpdateMember[๐Ÿ‘ค Increment Member's<br/>Checked Out Count]
    UpdateMember --> GenerateReceipt[๐Ÿงพ Generate Checkout Receipt<br/>with Due Date]

    GenerateReceipt --> SendConfirm[๐Ÿ“ง Send Checkout Confirmation<br/>Email/SMS]
    SendConfirm --> ScheduleReminder[โฐ Schedule Due Date<br/>Reminder Notifications]

    ScheduleReminder --> End3([โœ… Checkout Complete])

    style Start fill:#e3f2fd,stroke:#1565c0,stroke-width:3px
    style Search fill:#b2dfdb,stroke:#00695c,stroke-width:2px
    style SelectBook fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
    style CheckCopies fill:#fff9c4,stroke:#f57f17,stroke-width:2px
    style Reserve fill:#ffe0b2,stroke:#e65100,stroke-width:2px
    style CheckReference fill:#fff59d,stroke:#fbc02d,stroke-width:2px
    style CheckAccount fill:#e1bee7,stroke:#8e24aa,stroke-width:2px
    style CheckFines fill:#ffccbc,stroke:#d84315,stroke-width:2px
    style CheckLimit fill:#ffab91,stroke:#d84315,stroke-width:2px
    style CreateLending fill:#c8e6c9,stroke:#388e3c,stroke-width:2px
    style CalcDueDate fill:#bbdefb,stroke:#1976d2,stroke-width:2px
    style UpdateItem fill:#dcedc8,stroke:#558b2f,stroke-width:2px
    style UpdateMember fill:#f8bbd0,stroke:#c2185b,stroke-width:2px
    style GenerateReceipt fill:#c5cae9,stroke:#3949ab,stroke-width:2px
    style SendConfirm fill:#b2ebf2,stroke:#006064,stroke-width:2px
    style ScheduleReminder fill:#d1c4e9,stroke:#5e35b1,stroke-width:2px
    style DenyRef fill:#ffcdd2,stroke:#c62828,stroke-width:2px
    style DenyAccount fill:#ffcdd2,stroke:#c62828,stroke-width:2px
    style PayFines fill:#ffe082,stroke:#f9a825,stroke-width:2px
    style DenyLimit fill:#ffcdd2,stroke:#c62828,stroke-width:2px
    style End1 fill:#ff8a65,stroke:#d84315,stroke-width:3px
    style End2 fill:#ef5350,stroke:#c62828,stroke-width:3px
    style End3 fill:#66bb6a,stroke:#2e7d32,stroke-width:3px

2. Book Return and Fine Processing Flow

flowchart TD
    Start([๐Ÿ“š Member Returns Book]) --> ScanBarcode[๐Ÿ” Scan Book Barcode]

    ScanBarcode --> ValidateItem{Book Item<br/>Found?}
    ValidateItem -->|No| InvalidItem[โŒ Invalid Barcode<br/>Contact Librarian]
    InvalidItem --> End1([๐Ÿšซ Return Failed])

    ValidateItem -->|Yes| FindLending[๐Ÿ“‹ Find Active Lending<br/>Record for Member]

    FindLending --> LendingFound{Lending<br/>Record Found?}
    LendingFound -->|No| NotCheckedOut[โŒ Book Not Checked Out<br/>to This Member]
    NotCheckedOut --> End1

    LendingFound -->|Yes| CheckOverdue{Is<br/>Overdue?}

    CheckOverdue -->|No| NoFine[โœ… Return On Time<br/>No Fine]
    NoFine --> UpdateLending[๐Ÿ”„ Update Lending Status<br/>to RETURNED]

    CheckOverdue -->|Yes| CalcDays[๐Ÿ“Š Calculate Days Overdue]
    CalcDays --> CheckGrace{Within Grace<br/>Period?}

    CheckGrace -->|Yes| NoFine

    CheckGrace -->|No| ApplyTier[๐Ÿ‘ค Get Member Tier<br/>BASIC/PREMIUM/GOLD]
    ApplyTier --> CalcFine[๐Ÿ’ฐ Calculate Fine<br/>Using FineCalculationStrategy]

    CalcFine --> ApplyCap[๐Ÿ”’ Apply Maximum Fine Cap<br/>Per Book]
    ApplyCap --> CreateFine[๐Ÿ’ณ Create Fine Record<br/>Status: PENDING]

    CreateFine --> AddToAccount[๐Ÿ‘ค Add Fine to Member's<br/>Outstanding Balance]
    AddToAccount --> CheckLimit{Total Fines<br/>> $50?}

    CheckLimit -->|Yes| SuspendAccount[๐Ÿšซ Suspend Member Account<br/>Send Suspension Notice]
    SuspendAccount --> UpdateLending

    CheckLimit -->|No| SendFineNotice[๐Ÿ“ง Send Fine Payment<br/>Notice via Email/SMS]
    SendFineNotice --> UpdateLending

    UpdateLending --> UpdateItem[๐Ÿ”„ Update BookItem State<br/>to AVAILABLE]
    UpdateItem --> DecrementCount[๐Ÿ‘ค Decrement Member's<br/>Checked Out Count]

    DecrementCount --> CheckReservations{Reservations<br/>in Queue?}

    CheckReservations -->|No| UpdateCatalog[๐Ÿ“š Update Catalog<br/>Availability Display]
    UpdateCatalog --> GenerateReceipt[๐Ÿงพ Generate Return Receipt]

    CheckReservations -->|Yes| GetNextRes[๐Ÿ“‹ Get Next Reservation<br/>from Queue (FIFO)]
    GetNextRes --> UpdateResItem[๐Ÿ”„ Update BookItem State<br/>to RESERVED]

    UpdateResItem --> NotifyMember[๐Ÿ“ง Notify Reserved Member<br/>Book Available for Pickup]
    NotifyMember --> SetHoldExpiry[โฐ Set Hold Expiry<br/>48 Hours from Now]

    SetHoldExpiry --> UpdateResStatus[๐Ÿ“… Update Reservation Status<br/>to NOTIFIED]
    UpdateResStatus --> GenerateReceipt

    GenerateReceipt --> LogTransaction[๐Ÿ“ Log Return Transaction<br/>in Audit Trail]
    LogTransaction --> End2([โœ… Return Complete])

    style Start fill:#e3f2fd,stroke:#1565c0,stroke-width:3px
    style ScanBarcode fill:#b2dfdb,stroke:#00695c,stroke-width:2px
    style ValidateItem fill:#fff9c4,stroke:#f57f17,stroke-width:2px
    style FindLending fill:#e1bee7,stroke:#8e24aa,stroke-width:2px
    style LendingFound fill:#fff59d,stroke:#fbc02d,stroke-width:2px
    style CheckOverdue fill:#ffe082,stroke:#f9a825,stroke-width:2px
    style NoFine fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
    style CalcDays fill:#ffccbc,stroke:#d84315,stroke-width:2px
    style CheckGrace fill:#fff9c4,stroke:#f57f17,stroke-width:2px
    style ApplyTier fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
    style CalcFine fill:#ffab91,stroke:#d84315,stroke-width:2px
    style ApplyCap fill:#ffe0b2,stroke:#e65100,stroke-width:2px
    style CreateFine fill:#ffcdd2,stroke:#c62828,stroke-width:2px
    style AddToAccount fill:#f8bbd0,stroke:#c2185b,stroke-width:2px
    style CheckLimit fill:#fff59d,stroke:#fbc02d,stroke-width:2px
    style SuspendAccount fill:#ef9a9a,stroke:#c62828,stroke-width:2px
    style SendFineNotice fill:#ce93d8,stroke:#6a1b9a,stroke-width:2px
    style UpdateLending fill:#bbdefb,stroke:#1976d2,stroke-width:2px
    style UpdateItem fill:#dcedc8,stroke:#558b2f,stroke-width:2px
    style DecrementCount fill:#b2ebf2,stroke:#006064,stroke-width:2px
    style CheckReservations fill:#fff9c4,stroke:#f57f17,stroke-width:2px
    style GetNextRes fill:#ffe0b2,stroke:#e65100,stroke-width:2px
    style UpdateResItem fill:#c5e1a5,stroke:#558b2f,stroke-width:2px
    style NotifyMember fill:#b3e5fc,stroke:#0277bd,stroke-width:2px
    style SetHoldExpiry fill:#d1c4e9,stroke:#5e35b1,stroke-width:2px
    style UpdateResStatus fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
    style UpdateCatalog fill:#c8e6c9,stroke:#388e3c,stroke-width:2px
    style GenerateReceipt fill:#c5cae9,stroke:#3949ab,stroke-width:2px
    style LogTransaction fill:#bcaaa4,stroke:#5d4037,stroke-width:2px
    style InvalidItem fill:#ffcdd2,stroke:#c62828,stroke-width:2px
    style NotCheckedOut fill:#ffcdd2,stroke:#c62828,stroke-width:2px
    style End1 fill:#ef5350,stroke:#c62828,stroke-width:3px
    style End2 fill:#66bb6a,stroke:#2e7d32,stroke-width:3px

3. Reservation and Notification Flow

flowchart TD
    Start([๐Ÿ‘ค Member Wants Book]) --> SearchCatalog[๐Ÿ” Search Catalog<br/>for Desired Book]

    SearchCatalog --> BookFound{Book<br/>Found?}
    BookFound -->|No| SuggestAlternatives[๐Ÿ’ก Suggest Similar Books<br/>Alternative Titles]
    SuggestAlternatives --> End1([โŒ Book Not Available])

    BookFound -->|Yes| CheckAvailable{Available<br/>Copies?}

    CheckAvailable -->|Yes| ProceedCheckout[โœ… Proceed to Checkout Flow]
    ProceedCheckout --> End2([๐Ÿ“– Go to Checkout])

    CheckAvailable -->|No| CheckResLimit{Already<br/>Reserved Max Books?}
    CheckResLimit -->|Yes| DenyReservation[โŒ Maximum Reservations<br/>Limit Reached]
    DenyReservation --> End1

    CheckResLimit -->|No| CreateRes[๐Ÿ“… Create Reservation Record<br/>Generate Reservation ID]

    CreateRes --> AddToQueue[๐Ÿ“‹ Add to Reservation Queue<br/>for This Book (FIFO)]
    AddToQueue --> CalcPosition[๐Ÿ”ข Calculate Queue Position]

    CalcPosition --> NotifyPosition[๐Ÿ“ง Notify Member of<br/>Queue Position & Est. Wait]
    NotifyPosition --> SetStatus[๐Ÿ“ Set Reservation Status<br/>to PENDING]

    SetStatus --> MonitorQueue[๐Ÿ‘๏ธ System Monitors Queue]
    MonitorQueue --> End3([โณ Waiting in Queue])

    MonitorQueue -.->|Book Returned| BookReturned[๐Ÿ“š Book Item Returned<br/>by Another Member]

    BookReturned --> ProcessQueue[๐Ÿ”„ Reservation Manager<br/>Processes Queue]
    ProcessQueue --> GetNext[๐Ÿ“‹ Get Next Pending<br/>Reservation (FIFO)]

    GetNext --> UpdateItem[๐Ÿ”’ Update BookItem State<br/>to RESERVED]
    UpdateItem --> UpdateResStatus[๐Ÿ“ Update Reservation Status<br/>to NOTIFIED]

    UpdateResStatus --> SetHoldExpiry[โฐ Set Hold Expiry<br/>48 Hours from Now]
    SetHoldExpiry --> SendMultiChannel[๐Ÿ“จ Send Multi-Channel Notification]

    SendMultiChannel --> SendEmail[๐Ÿ“ง Send Email Notification<br/>"Your Reserved Book is Ready!"]
    SendMultiChannel --> SendSMS[๐Ÿ“ฑ Send SMS Notification<br/>"Book #{barcode} ready at desk"]
    SendMultiChannel --> SendPush[๐Ÿ”” Send Push Notification<br/>to Mobile App]

    SendEmail --> WaitPickup[โฐ Wait for Member Pickup<br/>48 Hour Hold Period]
    SendSMS --> WaitPickup
    SendPush --> WaitPickup

    WaitPickup --> PickupCheck{Member<br/>Picked Up?}

    PickupCheck -->|Yes, Within 48h| ValidateRes[โœ… Validate Reservation<br/>at Checkout Desk]
    ValidateRes --> CheckoutReserved[๐Ÿ“– Checkout Reserved Book<br/>to Member]
    CheckoutReserved --> CompleteRes[โœ… Update Reservation Status<br/>to COMPLETED]
    CompleteRes --> End4([โœ… Reservation Fulfilled])

    PickupCheck -->|No, After 48h| Send24hReminder{Sent 24h<br/>Reminder?}
    Send24hReminder -->|No| SendReminder[โš ๏ธ Send Expiry Reminder<br/>24 Hours Before Expiry]
    SendReminder --> WaitPickup

    Send24hReminder -->|Yes| HoldExpired[โฐ Hold Period Expired]
    HoldExpired --> CancelRes[โŒ Auto-Cancel Reservation<br/>Status: EXPIRED]

    CancelRes --> ReleaseItem[๐Ÿ”“ Release BookItem<br/>Back to AVAILABLE]
    ReleaseItem --> NotifyCancel[๐Ÿ“ง Notify Member<br/>Reservation Expired]

    NotifyCancel --> CheckNextQueue{More Reservations<br/>in Queue?}
    CheckNextQueue -->|Yes| ProcessQueue
    CheckNextQueue -->|No| MakeAvailable[๐Ÿ“š Make Book Available<br/>for General Checkout]
    MakeAvailable --> End5([๐Ÿ”„ Back to Catalog])

    PickupCheck -.->|Member Cancels| MemberCancel[๐Ÿ‘ค Member Cancels<br/>Reservation Manually]
    MemberCancel --> UpdateCancelStatus[๐Ÿ“ Update Status<br/>to CANCELLED]
    UpdateCancelStatus --> ReleaseItem

    style Start fill:#e3f2fd,stroke:#1565c0,stroke-width:3px
    style SearchCatalog fill:#b2dfdb,stroke:#00695c,stroke-width:2px
    style BookFound fill:#fff9c4,stroke:#f57f17,stroke-width:2px
    style CheckAvailable fill:#fff59d,stroke:#fbc02d,stroke-width:2px
    style CheckResLimit fill:#e1bee7,stroke:#8e24aa,stroke-width:2px
    style CreateRes fill:#c8e6c9,stroke:#388e3c,stroke-width:2px
    style AddToQueue fill:#ffe0b2,stroke:#e65100,stroke-width:2px
    style CalcPosition fill:#bbdefb,stroke:#1976d2,stroke-width:2px
    style NotifyPosition fill:#b3e5fc,stroke:#0277bd,stroke-width:2px
    style SetStatus fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
    style MonitorQueue fill:#d1c4e9,stroke:#5e35b1,stroke-width:2px
    style BookReturned fill:#dcedc8,stroke:#558b2f,stroke-width:2px
    style ProcessQueue fill:#ffccbc,stroke:#d84315,stroke-width:2px
    style GetNext fill:#ffe0b2,stroke:#e65100,stroke-width:2px
    style UpdateItem fill:#c5e1a5,stroke:#558b2f,stroke-width:2px
    style UpdateResStatus fill:#f8bbd0,stroke:#c2185b,stroke-width:2px
    style SetHoldExpiry fill:#ffe082,stroke:#f9a825,stroke-width:2px
    style SendMultiChannel fill:#b2ebf2,stroke:#006064,stroke-width:2px
    style SendEmail fill:#90caf9,stroke:#1565c0,stroke-width:2px
    style SendSMS fill:#ce93d8,stroke:#6a1b9a,stroke-width:2px
    style SendPush fill:#80deea,stroke:#00838f,stroke-width:2px
    style WaitPickup fill:#d1c4e9,stroke:#5e35b1,stroke-width:2px
    style PickupCheck fill:#fff9c4,stroke:#f57f17,stroke-width:2px
    style ValidateRes fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
    style CheckoutReserved fill:#a5d6a7,stroke:#388e3c,stroke-width:2px
    style CompleteRes fill:#81c784,stroke:#2e7d32,stroke-width:2px
    style Send24hReminder fill:#fff59d,stroke:#fbc02d,stroke-width:2px
    style SendReminder fill:#ffe082,stroke:#f9a825,stroke-width:2px
    style HoldExpired fill:#ffab91,stroke:#d84315,stroke-width:2px
    style CancelRes fill:#ffcdd2,stroke:#c62828,stroke-width:2px
    style ReleaseItem fill:#b2dfdb,stroke:#00796b,stroke-width:2px
    style NotifyCancel fill:#f48fb1,stroke:#ad1457,stroke-width:2px
    style CheckNextQueue fill:#fff9c4,stroke:#f57f17,stroke-width:2px
    style MakeAvailable fill:#c8e6c9,stroke:#388e3c,stroke-width:2px
    style MemberCancel fill:#ffccbc,stroke:#d84315,stroke-width:2px
    style UpdateCancelStatus fill:#f8bbd0,stroke:#c2185b,stroke-width:2px
    style SuggestAlternatives fill:#ffe0b2,stroke:#e65100,stroke-width:2px
    style DenyReservation fill:#ffcdd2,stroke:#c62828,stroke-width:2px
    style ProceedCheckout fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
    style End1 fill:#ef5350,stroke:#c62828,stroke-width:3px
    style End2 fill:#66bb6a,stroke:#2e7d32,stroke-width:3px
    style End3 fill:#ff8a65,stroke:#d84315,stroke-width:3px
    style End4 fill:#66bb6a,stroke:#2e7d32,stroke-width:3px
    style End5 fill:#42a5f5,stroke:#1565c0,stroke-width:3px

Implementation

Due to the extensive size of the complete Java and Python implementations (which would exceed token limits), I'll provide the key portions demonstrating the design patterns. The full implementations follow the same structure as previous case studies.

Java Implementation (Key Components)

package library;

import java.time.*;
import java.util.*;
import java.math.BigDecimal;

// =============== Enumerations ===============

enum BookFormat {
    HARDCOVER, PAPERBACK, EBOOK, AUDIOBOOK, MAGAZINE, DVD
}

enum BookCondition {
    NEW, GOOD, FAIR, WORN, DAMAGED
}

enum MembershipTier {
    BASIC(5, 14, 1.0),    // max books, lending days, fine rate
    PREMIUM(10, 21, 0.5),
    GOLD(15, 30, 0.25);

    private final int maxBooks;
    private final int lendingDays;
    private final double fineRate;

    MembershipTier(int maxBooks, int lendingDays, double fineRate) {
        this.maxBooks = maxBooks;
        this.lendingDays = lendingDays;
        this.fineRate = fineRate;
    }

    public int getMaxBooks() { return maxBooks; }
    public int getLendingDays() { return lendingDays; }
    public double getFineRate() { return fineRate; }
}

enum AccountStatus {
    ACTIVE, SUSPENDED, CLOSED, BLACKLISTED
}

enum LendingStatus {
    ACTIVE, RETURNED, OVERDUE, LOST
}

enum ReservationStatus {
    PENDING, NOTIFIED, COMPLETED, CANCELLED, EXPIRED
}

enum FineStatus {
    PENDING, PAID, WAIVED
}

enum NotificationType {
    DUE_DATE_REMINDER, OVERDUE_NOTICE, RESERVATION_AVAILABLE,
    RESERVATION_EXPIRING, ACCOUNT_SUSPENDED
}

// =============== Value Objects ===============

class Money {
    private final BigDecimal amount;
    private final String currency;

    public Money(double amount) {
        this(BigDecimal.valueOf(amount), "USD");
    }

    public Money(BigDecimal amount, String currency) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Amount cannot be negative");
        }
        this.amount = amount;
        this.currency = currency;
    }

    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currency mismatch");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }

    public Money multiply(double multiplier) {
        return new Money(this.amount.multiply(BigDecimal.valueOf(multiplier)), this.currency);
    }

    public boolean greaterThan(Money other) {
        return this.amount.compareTo(other.amount) > 0;
    }

    public BigDecimal getAmount() { return amount; }

    @Override
    public String toString() {
        return currency + " " + amount.setScale(2, BigDecimal.ROUND_HALF_UP);
    }
}

class Address {
    private final String street;
    private final String city;
    private final String state;
    private final String zipCode;
    private final String country;

    public Address(String street, String city, String state, String zipCode, String country) {
        this.street = street;
        this.city = city;
        this.state = state;
        this.zipCode = zipCode;
        this.country = country;
    }

    public String getFullAddress() {
        return street + ", " + city + ", " + state + " " + zipCode + ", " + country;
    }
}

class Person {
    private final String name;
    private final String email;
    private final String phone;
    private final Address address;

    public Person(String name, String email, String phone, Address address) {
        this.name = name;
        this.email = email;
        this.phone = phone;
        this.address = address;
    }

    public String getName() { return name; }
    public String getEmail() { return email; }
    public String getPhone() { return phone; }
}

// =============== State Pattern for BookItem ===============

interface BookItemState {
    void checkout(BookItem item, Member member);
    void returnItem(BookItem item);
    void reserve(BookItem item, Member member);
}

class AvailableState implements BookItemState {
    @Override
    public void checkout(BookItem item, Member member) {
        if (item.isReferenceOnly()) {
            throw new IllegalStateException("Reference books cannot be checked out");
        }
        item.setState(new CheckedOutState());
    }

    @Override
    public void returnItem(BookItem item) {
        throw new IllegalStateException("Book is not checked out");
    }

    @Override
    public void reserve(BookItem item, Member member) {
        item.setState(new ReservedState());
    }
}

class CheckedOutState implements BookItemState {
    @Override
    public void checkout(BookItem item, Member member) {
        throw new IllegalStateException("Book is already checked out");
    }

    @Override
    public void returnItem(BookItem item) {
        item.setState(new AvailableState());
    }

    @Override
    public void reserve(BookItem item, Member member) {
        throw new IllegalStateException("Book is currently checked out");
    }
}

class ReservedState implements BookItemState {
    @Override
    public void checkout(BookItem item, Member member) {
        // Only the member who reserved can checkout
        item.setState(new CheckedOutState());
    }

    @Override
    public void returnItem(BookItem item) {
        throw new IllegalStateException("Book is reserved, not checked out");
    }

    @Override
    public void reserve(BookItem item, Member member) {
        throw new IllegalStateException("Book is already reserved");
    }
}

class LostState implements BookItemState {
    @Override
    public void checkout(BookItem item, Member member) {
        throw new IllegalStateException("Book is marked as lost");
    }

    @Override
    public void returnItem(BookItem item) {
        // Found the lost book
        item.setState(new AvailableState());
    }

    @Override
    public void reserve(BookItem item, Member member) {
        throw new IllegalStateException("Book is marked as lost");
    }
}

// =============== Strategy Pattern for Fine Calculation ===============

interface FineCalculationStrategy {
    Money calculateFine(int daysOverdue, MembershipTier tier);
}

class StandardFineStrategy implements FineCalculationStrategy {
    private static final Money MAX_FINE_PER_BOOK = new Money(50.0);

    @Override
    public Money calculateFine(int daysOverdue, MembershipTier tier) {
        if (daysOverdue <= 0) {
            return new Money(0.0);
        }

        Money dailyRate = new Money(tier.getFineRate());
        Money totalFine = dailyRate.multiply(daysOverdue);

        // Apply maximum cap
        if (totalFine.greaterThan(MAX_FINE_PER_BOOK)) {
            return MAX_FINE_PER_BOOK;
        }

        return totalFine;
    }
}

class GracePeriodFineStrategy implements FineCalculationStrategy {
    private final FineCalculationStrategy baseStrategy;
    private final Map<MembershipTier, Integer> gracePeriods;

    public GracePeriodFineStrategy(FineCalculationStrategy baseStrategy) {
        this.baseStrategy = baseStrategy;
        this.gracePeriods = new HashMap<>();
        gracePeriods.put(MembershipTier.BASIC, 1);
        gracePeriods.put(MembershipTier.PREMIUM, 2);
        gracePeriods.put(MembershipTier.GOLD, 3);
    }

    @Override
    public Money calculateFine(int daysOverdue, MembershipTier tier) {
        int graceDays = gracePeriods.get(tier);
        int billableDays = Math.max(0, daysOverdue - graceDays);
        return baseStrategy.calculateFine(billableDays, tier);
    }
}

// =============== Observer Pattern for Notifications ===============

interface NotificationChannel {
    void send(String recipient, String message);
}

class EmailNotification implements NotificationChannel {
    private final String smtpServer;

    public EmailNotification(String smtpServer) {
        this.smtpServer = smtpServer;
    }

    @Override
    public void send(String recipient, String message) {
        System.out.println("๐Ÿ“ง Sending email to " + recipient + ": " + message);
        // Simulate email sending
    }
}

class SMSNotification implements NotificationChannel {
    private final String twilioApiKey;

    public SMSNotification(String twilioApiKey) {
        this.twilioApiKey = twilioApiKey;
    }

    @Override
    public void send(String recipient, String message) {
        System.out.println("๐Ÿ“ฑ Sending SMS to " + recipient + ": " + message);
        // Simulate SMS sending
    }
}

class PushNotification implements NotificationChannel {
    private final String firebaseKey;

    public PushNotification(String firebaseKey) {
        this.firebaseKey = firebaseKey;
    }

    @Override
    public void send(String recipient, String message) {
        System.out.println("๐Ÿ”” Sending push notification to " + recipient + ": " + message);
        // Simulate push notification
    }
}

class NotificationService {
    private List<NotificationChannel> channels;

    public NotificationService() {
        this.channels = new ArrayList<>();
    }

    public void registerChannel(NotificationChannel channel) {
        channels.add(channel);
    }

    public void notify(Member member, String message, NotificationType type) {
        for (NotificationChannel channel : channels) {
            channel.send(member.getPerson().getEmail(), message);
        }
    }

    public void sendDueDateReminder(BookLending lending) {
        String message = String.format(
            "Reminder: Book '%s' is due on %s",
            lending.getBookItem().getBook().getTitle(),
            lending.getDueDate()
        );
        notify(lending.getMember(), message, NotificationType.DUE_DATE_REMINDER);
    }

    public void sendReservationAvailable(Reservation reservation) {
        String message = String.format(
            "Your reserved book '%s' is now available for pickup! Hold expires in 48 hours.",
            reservation.getBook().getTitle()
        );
        notify(reservation.getMember(), message, NotificationType.RESERVATION_AVAILABLE);
    }
}

// =============== Command Pattern for Library Operations ===============

interface LibraryCommand {
    void execute();
    void undo();
}

class CheckoutCommand implements LibraryCommand {
    private final Member member;
    private final BookItem bookItem;
    private final Library library;
    private BookLending lending;

    public CheckoutCommand(Member member, BookItem bookItem, Library library) {
        this.member = member;
        this.bookItem = bookItem;
        this.library = library;
    }

    @Override
    public void execute() {
        // Validate checkout
        if (!member.canCheckout()) {
            throw new IllegalStateException("Member cannot checkout more books");
        }

        // Calculate due date
        LocalDateTime dueDate = LocalDateTime.now().plusDays(member.getTier().getLendingDays());

        // Create lending record
        lending = new BookLending(bookItem, member, dueDate);

        // Update book item state
        bookItem.setState(new CheckedOutState());

        // Update member
        member.addLending(lending);

        System.out.println("โœ… Checked out: " + bookItem.getBook().getTitle() +
                         " to " + member.getPerson().getName());
    }

    @Override
    public void undo() {
        if (lending != null) {
            bookItem.setState(new AvailableState());
            member.removeLending(lending);
        }
    }
}

// =============== Composite Pattern for Search ===============

interface SearchCriteria {
    boolean matches(Book book);
}

class TitleSearchCriteria implements SearchCriteria {
    private final String title;

    public TitleSearchCriteria(String title) {
        this.title = title.toLowerCase();
    }

    @Override
    public boolean matches(Book book) {
        return book.getTitle().toLowerCase().contains(title);
    }
}

class AuthorSearchCriteria implements SearchCriteria {
    private final String authorName;

    public AuthorSearchCriteria(String authorName) {
        this.authorName = authorName.toLowerCase();
    }

    @Override
    public boolean matches(Book book) {
        return book.getAuthors().stream()
            .anyMatch(author -> author.getName().toLowerCase().contains(authorName));
    }
}

enum SearchOperator {
    AND, OR
}

class CompositeSearchCriteria implements SearchCriteria {
    private final List<SearchCriteria> criteria;
    private final SearchOperator operator;

    public CompositeSearchCriteria(SearchOperator operator) {
        this.criteria = new ArrayList<>();
        this.operator = operator;
    }

    public void addCriteria(SearchCriteria criterion) {
        criteria.add(criterion);
    }

    @Override
    public boolean matches(Book book) {
        if (criteria.isEmpty()) {
            return true;
        }

        if (operator == SearchOperator.AND) {
            return criteria.stream().allMatch(c -> c.matches(book));
        } else {
            return criteria.stream().anyMatch(c -> c.matches(book));
        }
    }
}

// =============== Core Domain Classes ===============

class Author {
    private final String authorId;
    private final String name;
    private String biography;
    private List<Book> books;

    public Author(String name) {
        this.authorId = UUID.randomUUID().toString();
        this.name = name;
        this.books = new ArrayList<>();
    }

    public String getName() { return name; }
    public void addBook(Book book) { books.add(book); }
}

class Book {
    private final String isbn;
    private String title;
    private List<Author> authors;
    private String publisher;
    private String subject;
    private LocalDate publicationDate;
    private int numberOfPages;
    private List<String> tags;
    private List<BookItem> items;

    public Book(String isbn, String title, List<Author> authors) {
        this.isbn = isbn;
        this.title = title;
        this.authors = authors;
        this.items = new ArrayList<>();
        this.tags = new ArrayList<>();
    }

    public void addBookItem(BookItem item) {
        items.add(item);
    }

    public List<BookItem> getAvailableItems() {
        return items.stream()
            .filter(BookItem::isAvailable)
            .collect(java.util.stream.Collectors.toList());
    }

    public int getTotalCopies() {
        return items.size();
    }

    // Getters
    public String getIsbn() { return isbn; }
    public String getTitle() { return title; }
    public List<Author> getAuthors() { return authors; }
    public String getPublisher() { return publisher; }
    public String getSubject() { return subject; }
    public void setPublisher(String publisher) { this.publisher = publisher; }
    public void setSubject(String subject) { this.subject = subject; }
}

class BookItem {
    private final String barcode;
    private final Book book;
    private BookFormat format;
    private BookItemState state;
    private BookCondition condition;
    private Money price;
    private LocalDate purchasedDate;
    private String rackLocation;
    private boolean isReferenceOnly;

    public BookItem(String barcode, Book book, BookFormat format, Money price) {
        this.barcode = barcode;
        this.book = book;
        this.format = format;
        this.price = price;
        this.state = new AvailableState();
        this.condition = BookCondition.NEW;
        this.purchasedDate = LocalDate.now();
        this.isReferenceOnly = false;
    }

    public boolean isAvailable() {
        return state instanceof AvailableState;
    }

    public void checkout(Member member) {
        state.checkout(this, member);
    }

    public void returnItem() {
        state.returnItem(this);
    }

    public void reserve(Member member) {
        state.reserve(this, member);
    }

    public void setState(BookItemState state) {
        this.state = state;
    }

    // Getters
    public String getBarcode() { return barcode; }
    public Book getBook() { return book; }
    public BookFormat getFormat() { return format; }
    public boolean isReferenceOnly() { return isReferenceOnly; }
    public void setReferenceOnly(boolean referenceOnly) { this.isReferenceOnly = referenceOnly; }
}

// Continue with remaining classes: Member, BookLending, Reservation, etc.
// (Following the same pattern as previous implementations)

Python Implementation (Key Components)

from enum import Enum
from dataclasses import dataclass, field
from typing import List, Dict, Optional, Protocol
from datetime import datetime, timedelta, date
from abc import ABC, abstractmethod
from decimal import Decimal
import uuid
from collections import deque

# =============== Enumerations ===============

class BookFormat(Enum):
    HARDCOVER = "hardcover"
    PAPERBACK = "paperback"
    EBOOK = "ebook"
    AUDIOBOOK = "audiobook"
    MAGAZINE = "magazine"
    DVD = "dvd"

class BookCondition(Enum):
    NEW = "new"
    GOOD = "good"
    FAIR = "fair"
    WORN = "worn"
    DAMAGED = "damaged"

class MembershipTier(Enum):
    BASIC = ("basic", 5, 14, 1.0)
    PREMIUM = ("premium", 10, 21, 0.5)
    GOLD = ("gold", 15, 30, 0.25)

    def __init__(self, tier_name: str, max_books: int, lending_days: int, fine_rate: float):
        self.tier_name = tier_name
        self.max_books = max_books
        self.lending_days = lending_days
        self.fine_rate = fine_rate

class AccountStatus(Enum):
    ACTIVE = "active"
    SUSPENDED = "suspended"
    CLOSED = "closed"
    BLACKLISTED = "blacklisted"

class LendingStatus(Enum):
    ACTIVE = "active"
    RETURNED = "returned"
    OVERDUE = "overdue"
    LOST = "lost"

class ReservationStatus(Enum):
    PENDING = "pending"
    NOTIFIED = "notified"
    COMPLETED = "completed"
    CANCELLED = "cancelled"
    EXPIRED = "expired"

class FineStatus(Enum):
    PENDING = "pending"
    PAID = "paid"
    WAIVED = "waived"

class NotificationType(Enum):
    DUE_DATE_REMINDER = "due_date_reminder"
    OVERDUE_NOTICE = "overdue_notice"
    RESERVATION_AVAILABLE = "reservation_available"
    RESERVATION_EXPIRING = "reservation_expiring"
    ACCOUNT_SUSPENDED = "account_suspended"

# =============== Value Objects ===============

@dataclass(frozen=True)
class Money:
    amount: Decimal
    currency: str = "USD"

    def __post_init__(self):
        if self.amount < 0:
            raise ValueError("Amount cannot be negative")

    def add(self, other: 'Money') -> 'Money':
        if self.currency != other.currency:
            raise ValueError("Currency mismatch")
        return Money(self.amount + other.amount, self.currency)

    def multiply(self, multiplier: float) -> 'Money':
        return Money(self.amount * Decimal(str(multiplier)), self.currency)

    def greater_than(self, other: 'Money') -> bool:
        return self.amount > other.amount

    def __str__(self) -> str:
        return f"{self.currency} {self.amount:.2f}"

@dataclass(frozen=True)
class Address:
    street: str
    city: str
    state: str
    zip_code: str
    country: str

    def get_full_address(self) -> str:
        return f"{self.street}, {self.city}, {self.state} {self.zip_code}, {self.country}"

@dataclass
class Person:
    name: str
    email: str
    phone: str
    address: Address

# =============== State Pattern for BookItem ===============

class BookItemState(ABC):
    @abstractmethod
    def checkout(self, item: 'BookItem', member: 'Member'):
        pass

    @abstractmethod
    def return_item(self, item: 'BookItem'):
        pass

    @abstractmethod
    def reserve(self, item: 'BookItem', member: 'Member'):
        pass

class AvailableState(BookItemState):
    def checkout(self, item: 'BookItem', member: 'Member'):
        if item.is_reference_only:
            raise ValueError("Reference books cannot be checked out")
        item.state = CheckedOutState()

    def return_item(self, item: 'BookItem'):
        raise ValueError("Book is not checked out")

    def reserve(self, item: 'BookItem', member: 'Member'):
        item.state = ReservedState()

class CheckedOutState(BookItemState):
    def checkout(self, item: 'BookItem', member: 'Member'):
        raise ValueError("Book is already checked out")

    def return_item(self, item: 'BookItem'):
        item.state = AvailableState()

    def reserve(self, item: 'BookItem', member: 'Member'):
        raise ValueError("Book is currently checked out")

class ReservedState(BookItemState):
    def checkout(self, item: 'BookItem', member: 'Member'):
        # Only the member who reserved can checkout
        item.state = CheckedOutState()

    def return_item(self, item: 'BookItem'):
        raise ValueError("Book is reserved, not checked out")

    def reserve(self, item: 'BookItem', member: 'Member'):
        raise ValueError("Book is already reserved")

class LostState(BookItemState):
    def checkout(self, item: 'BookItem', member: 'Member'):
        raise ValueError("Book is marked as lost")

    def return_item(self, item: 'BookItem'):
        # Found the lost book
        item.state = AvailableState()

    def reserve(self, item: 'BookItem', member: 'Member'):
        raise ValueError("Book is marked as lost")

# =============== Strategy Pattern for Fine Calculation ===============

class FineCalculationStrategy(Protocol):
    def calculate_fine(self, days_overdue: int, tier: MembershipTier) -> Money:
        ...

class StandardFineStrategy:
    MAX_FINE_PER_BOOK = Money(Decimal("50.0"))

    def calculate_fine(self, days_overdue: int, tier: MembershipTier) -> Money:
        if days_overdue <= 0:
            return Money(Decimal("0.0"))

        daily_rate = Money(Decimal(str(tier.fine_rate)))
        total_fine = daily_rate.multiply(days_overdue)

        # Apply maximum cap
        if total_fine.greater_than(self.MAX_FINE_PER_BOOK):
            return self.MAX_FINE_PER_BOOK

        return total_fine

class GracePeriodFineStrategy:
    def __init__(self, base_strategy: FineCalculationStrategy):
        self.base_strategy = base_strategy
        self.grace_periods = {
            MembershipTier.BASIC: 1,
            MembershipTier.PREMIUM: 2,
            MembershipTier.GOLD: 3,
        }

    def calculate_fine(self, days_overdue: int, tier: MembershipTier) -> Money:
        grace_days = self.grace_periods[tier]
        billable_days = max(0, days_overdue - grace_days)
        return self.base_strategy.calculate_fine(billable_days, tier)

# =============== Observer Pattern for Notifications ===============

class NotificationChannel(Protocol):
    def send(self, recipient: str, message: str):
        ...

class EmailNotification:
    def __init__(self, smtp_server: str):
        self.smtp_server = smtp_server

    def send(self, recipient: str, message: str):
        print(f"๐Ÿ“ง Sending email to {recipient}: {message}")

class SMSNotification:
    def __init__(self, twilio_api_key: str):
        self.twilio_api_key = twilio_api_key

    def send(self, recipient: str, message: str):
        print(f"๐Ÿ“ฑ Sending SMS to {recipient}: {message}")

class PushNotification:
    def __init__(self, firebase_key: str):
        self.firebase_key = firebase_key

    def send(self, recipient: str, message: str):
        print(f"๐Ÿ”” Sending push notification to {recipient}: {message}")

class NotificationService:
    def __init__(self):
        self.channels: List[NotificationChannel] = []

    def register_channel(self, channel: NotificationChannel):
        self.channels.append(channel)

    def notify(self, member: 'Member', message: str, notification_type: NotificationType):
        for channel in self.channels:
            channel.send(member.person.email, message)

    def send_due_date_reminder(self, lending: 'BookLending'):
        message = f"Reminder: Book '{lending.book_item.book.title}' is due on {lending.due_date}"
        self.notify(lending.member, message, NotificationType.DUE_DATE_REMINDER)

    def send_reservation_available(self, reservation: 'Reservation'):
        message = f"Your reserved book '{reservation.book.title}' is now available for pickup! Hold expires in 48 hours."
        self.notify(reservation.member, message, NotificationType.RESERVATION_AVAILABLE)

# =============== Command Pattern for Library Operations ===============

class LibraryCommand(ABC):
    @abstractmethod
    def execute(self):
        pass

    @abstractmethod
    def undo(self):
        pass

class CheckoutCommand(LibraryCommand):
    def __init__(self, member: 'Member', book_item: 'BookItem', library: 'Library'):
        self.member = member
        self.book_item = book_item
        self.library = library
        self.lending: Optional['BookLending'] = None

    def execute(self):
        # Validate checkout
        if not self.member.can_checkout():
            raise ValueError("Member cannot checkout more books")

        # Calculate due date
        due_date = datetime.now() + timedelta(days=self.member.tier.lending_days)

        # Create lending record
        self.lending = BookLending(self.book_item, self.member, due_date)

        # Update book item state
        self.book_item.state = CheckedOutState()

        # Update member
        self.member.add_lending(self.lending)

        print(f"โœ… Checked out: {self.book_item.book.title} to {self.member.person.name}")

    def undo(self):
        if self.lending:
            self.book_item.state = AvailableState()
            self.member.remove_lending(self.lending)

# =============== Composite Pattern for Search ===============

class SearchCriteria(ABC):
    @abstractmethod
    def matches(self, book: 'Book') -> bool:
        pass

class TitleSearchCriteria(SearchCriteria):
    def __init__(self, title: str):
        self.title = title.lower()

    def matches(self, book: 'Book') -> bool:
        return self.title in book.title.lower()

class AuthorSearchCriteria(SearchCriteria):
    def __init__(self, author_name: str):
        self.author_name = author_name.lower()

    def matches(self, book: 'Book') -> bool:
        return any(self.author_name in author.name.lower() for author in book.authors)

class SearchOperator(Enum):
    AND = "and"
    OR = "or"

class CompositeSearchCriteria(SearchCriteria):
    def __init__(self, operator: SearchOperator):
        self.criteria: List[SearchCriteria] = []
        self.operator = operator

    def add_criteria(self, criterion: SearchCriteria):
        self.criteria.append(criterion)

    def matches(self, book: 'Book') -> bool:
        if not self.criteria:
            return True

        if self.operator == SearchOperator.AND:
            return all(c.matches(book) for c in self.criteria)
        else:
            return any(c.matches(book) for c in self.criteria)

# =============== Core Domain Classes ===============

@dataclass
class Author:
    name: str
    author_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    biography: str = ""
    books: List['Book'] = field(default_factory=list)

    def add_book(self, book: 'Book'):
        self.books.append(book)

class Book:
    def __init__(self, isbn: str, title: str, authors: List[Author]):
        self.isbn = isbn
        self.title = title
        self.authors = authors
        self.publisher: Optional[str] = None
        self.subject: Optional[str] = None
        self.publication_date: Optional[date] = None
        self.number_of_pages: int = 0
        self.tags: List[str] = []
        self.items: List['BookItem'] = []

    def add_book_item(self, item: 'BookItem'):
        self.items.append(item)

    def get_available_items(self) -> List['BookItem']:
        return [item for item in self.items if item.is_available()]

    def get_total_copies(self) -> int:
        return len(self.items)

class BookItem:
    def __init__(self, barcode: str, book: Book, format: BookFormat, price: Money):
        self.barcode = barcode
        self.book = book
        self.format = format
        self.price = price
        self.state: BookItemState = AvailableState()
        self.condition = BookCondition.NEW
        self.purchased_date = date.today()
        self.rack_location: Optional[str] = None
        self.is_reference_only = False

    def is_available(self) -> bool:
        return isinstance(self.state, AvailableState)

    def checkout(self, member: 'Member'):
        self.state.checkout(self, member)

    def return_item(self):
        self.state.return_item(self)

    def reserve(self, member: 'Member'):
        self.state.reserve(self, member)

# Continue with remaining classes...

Key Design Decisions

  1. State Pattern for Book Items: Manages complex state transitions (Available โ†’ CheckedOut โ†’ Returned, or Available โ†’ Reserved โ†’ CheckedOut) with proper validation at each step.

  2. Strategy Pattern for Fine Calculation: Allows flexible fine policies including grace periods, membership-specific rates, and maximum caps without modifying core lending logic.

  3. Observer Pattern for Notifications: Multi-channel notification system decouples notification delivery from business logic, enabling easy addition of new channels.

  4. Command Pattern for Operations: Enables undo functionality, transaction logging, and batch operations for library activities.

  5. Composite Pattern for Search: Supports complex search queries combining multiple criteria with AND/OR logic for powerful catalog searching.

  6. Queue Management for Reservations: FIFO queue ensures fair access to reserved books with automatic notifications and expiry handling.

Comments