# Library System OOD: The One Relationship Interviewers Actually Test

<img src="https://bugfree-s3.s3.amazonaws.com/mermaid_diagrams/image_1769364958063.png" alt="Library System Diagram" style="max-width:700px;width:100%;height:auto;display:block;margin:0 auto 1rem;" />

In a Library Management System design, one decision separates a thoughtful OOD answer from a guess: model borrowing as a Transaction object — not as a boolean "available" flag on Book.

## The core idea

The Book's availability should be derived, not stored. The single source of truth is a Transaction that links Member ↔ Book and records borrowDate, dueDate, returnDate, and fine (if any). That Transaction object:

- Provides auditability: who borrowed what and when.
- Answers runtime questions: "Who has this book now?" or "What is this member currently holding?".
- Enables overdue and fine logic cleanly (compute based on dueDate and returnDate).
- Prevents inconsistent state when failures happen during return/checkout, because state changes happen inside a single transaction object rather than scattered flags.

If you can't clearly explain why Transaction is the core, you're designing by guessing — not by modeling the domain.

## Why not a Book.available flag?

- Derived state: availability is a result of active Transactions, not an independent truth.
- Race and failure conditions: toggling a flag during checkout/return can leave the system inconsistent if the process fails mid-way.
- No history: flags don't tell you who had the book previously or when it was returned.
- Complex queries become harder: answering "which member currently has book X?" is direct with Transactions but requires joins/extra bookkeeping with flags.

## What a Transaction typically contains

- transactionId
- bookId (or Book reference)
- memberId (or Member reference)
- borrowDate
- dueDate
- returnDate (nullable)
- fine (computed or stored)
- status (CHECKED_OUT, RETURNED, LOST, etc.)

Example (pseudo-code):

```java
class Transaction {
  UUID id;
  Member member;
  Book book;
  LocalDate borrowDate;
  LocalDate dueDate;
  LocalDate returnDate; // null until returned
  Money fine;
  TransactionStatus status; // CHECKED_OUT, RETURNED, LOST
}

// Derived availability
boolean isBookAvailable(Book book) {
  return !exists(Transaction t where t.book == book && t.status == CHECKED_OUT);
}
```

## How to explain this in an interview (quick script)

- State the thesis: "Borrowing should be modeled as a Transaction object, not a flag on Book."
- Explain the source-of-truth reasoning: "Availability is derived from active transactions."
- List benefits briefly: auditability, consistent state, simpler overdue/fine logic, easier queries.
- Optionally sketch the Transaction fields and how availability is derived.

That concise line — plus a quick example — demonstrates domain thinking and shows you understand consistency, auditability, and failure modes.

## Bottom line

Treat borrowing as a first-class Transaction. It's the relationship interviewers actually test: can you model domain state correctly and reason about derived state, consistency, and failures?

#SystemDesign #OOP #SoftwareEngineering
