Boundaries — Example: Library System

The Scenario

A public library system where patrons check out books, librarians manage the collection, overdue books generate fines, and the library sends reminders. Patrons can search the catalog and place holds on books that are currently checked out.


Step 1: List the Nouns

From the description and common sense about how libraries work:

  • Patron
  • Librarian
  • Book
  • Copy (a library owns multiple copies of the same book)
  • Catalog
  • Inventory
  • Checkout record
  • Due date
  • Return
  • Hold (a reservation on a book)
  • Hold queue (when multiple patrons want the same book)
  • Fine
  • Payment
  • Reminder (notification about due dates)
  • Library card
  • Account (patron's account)
  • Search query
  • Search results

18 nouns. This will NOT become 18 modules.


Step 2: Group by Responsibility

ConcernNounsRationale
The CollectionBook, Copy, Catalog, InventoryAll about what the library has
Patron AccountsPatron, Library Card, AccountAll about who uses the library
CirculationCheckout, Return, Due Date, Hold, Hold QueueAll about the movement of books between library and patron
FinancesFine, PaymentAll about money
CommunicationReminderAll about notifying patrons
SearchSearch Query, Search ResultsAll about finding books

Six concerns from 18 nouns. But are six the right number? Let's examine.

Should Search be its own boundary? Search operates on Catalog data, but it doesn't modify it. It's a read-only view with its own complexity (matching, ranking, filtering). Making it separate means the Catalog can change how books are stored without affecting how they're searched. Yes — keep it separate.

Should Communication be its own boundary? Reminders are triggered by Circulation events (book due soon, book overdue). Should reminders live inside Circulation? No — because the library might also want to send other communications later (new book announcements, event invitations). Keeping Communication separate lets it grow without changing Circulation. Yes — keep it separate.

Should Finances be its own boundary? Fines are triggered by Circulation, and payments come from Patrons. Finances sits between them. If we put fines inside Circulation, then Circulation needs to know about money, which is outside its core concern. Yes — keep Finances separate.


Step 3: Define Inside and Outside

Catalog Module

  • Inside: Adding books to the collection, removing books, tracking how many copies of each book exist, storing book details (title, author, ISBN, genre, description), tracking the physical condition of copies
  • Outside: Who has checked out which copy (that's Circulation), searching for books (that's Search), book prices or fines (that's Finances), patron information (that's Accounts)

Elevator test: "Manages the library's collection — what books exist and how many copies are available."

Patron Accounts Module

  • Inside: Creating patron accounts, issuing library cards, storing patron info (name, address, contact), verifying patron identity, managing account status (active, suspended, expired), storing patron preferences
  • Outside: What books a patron has checked out (that's Circulation), what fines they owe (that's Finances), sending emails to patrons (that's Communication)

Elevator test: "Manages who the library's patrons are and their account status."

Circulation Module

  • Inside: Recording checkouts (which patron, which copy, what date), calculating due dates, recording returns, managing the hold queue (who wants a book and in what order), enforcing checkout limits (max 10 books), tracking overdue status
  • Outside: Book details like title or author (that's Catalog), patron contact info (that's Accounts), fine amounts or payments (that's Finances), sending overdue notices (that's Communication)

Elevator test: "Tracks the movement of books between the library and patrons — who has what and when it's due."

Finances Module

  • Inside: Calculating fine amounts based on how overdue a book is, recording fine charges, processing payments, tracking patron balance (what they owe), applying fine policies (grace periods, maximum fines, waivers)
  • Outside: Whether a book is overdue (that's Circulation — Finances gets told), patron contact info (that's Accounts), book details (that's Catalog)

Elevator test: "Handles all money — what patrons owe, what they've paid, and fine policies."

Communication Module

  • Inside: Deciding how to contact a patron (email, text, postal), formatting messages, sending messages, tracking delivery success/failure, managing communication preferences (patron opts out of texts)
  • Outside: Deciding when to contact someone (other modules trigger this), determining what the patron owes (that's Finances), knowing what books are due (that's Circulation)

Elevator test: "Delivers messages to patrons through their preferred channel."

Search Module

  • Inside: Accepting search queries, matching against the catalog (by title, author, genre, ISBN, keyword), ranking results by relevance, filtering (available now, genre, format), paginating results
  • Outside: Modifying the catalog (that's Catalog), checking out books (that's Circulation), patron info (that's Accounts)

Elevator test: "Helps patrons find books in the collection."


Step 4: Connection Diagram

┌──────────────┐       "does this patron       ┌──────────────┐
│   Patron     │        exist and are            │ Circulation  │
│   Accounts   │◄──── they active?"─────────────│              │
│              │                                 │  - checkouts │
│  - identity  │                                 │  - returns   │
│  - status    │                                 │  - holds     │
└──────────────┘                                 │  - due dates │
       ▲                                         └──────────────┘
       │                                              │     │
       │ "who to contact"                             │     │
       │ + "how"                                      │     │
       │                                              │     │
┌──────────────┐      "this is overdue" ─────────────┘     │
│Communication │◄──── or "due date approaching"             │
│              │                                            │
│  - channels  │      "fine has been charged" ──┐           │
│  - delivery  │◄──── or "payment received"     │           │
└──────────────┘                                │           │
                                          ┌──────────────┐  │ "does this
                                          │  Finances    │  │  book exist?"
                                          │              │◄─┘  + "is a copy
                                          │  - fines     │     available?"
                                          │  - payments  │
                                          └──────────────┘
                                                            │
                    ┌──────────────┐                        │
                    │   Catalog    │◄───────────────────────┘
                    │              │
                    │  - books     │◄──── "search for"
                    │  - copies    │       books matching X
                    │  - details   │
                    └──────────────┘      ┌──────────────┐
                           ▲              │    Search    │
                           └──────────────│              │
                             read-only    │  - queries   │
                             access       │  - results   │
                                          └──────────────┘

Connection Analysis

Let's count the connections:

  • Circulation connects to: Accounts, Catalog, Finances, Communication = 4 connections
  • Communication connects to: Accounts, Circulation, Finances = 3 connections
  • Finances connects to: Circulation = 1 connection (plus triggered by Circulation)
  • Search connects to: Catalog = 1 connection (read-only)
  • Catalog connects to: nothing outbound (it's a foundational data store)
  • Accounts connects to: nothing outbound (it's a foundational data store)

The natural foundation pieces (Catalog, Accounts) have zero outbound dependencies — they don't need anything from the other modules. Everything else depends on them. This is a sign of good architecture: foundational data has no dependencies, and everything else reaches down to it.


Controversial Decisions and Tradeoffs

Should hold notifications come from Circulation or Communication?

When a hold becomes available (the book was returned), someone needs to tell the patron.

Option A: Circulation sends the notification directly.

  • Pro: Simpler — fewer module interactions
  • Con: Circulation now needs to know about email/text/postal preferences, which is outside its domain

Option B: Circulation emits an event ("hold available"), Communication picks it up, looks up the patron's contact preferences, and sends the message.

  • Pro: Each module stays within its boundary. Communication can be enhanced (add push notifications) without changing Circulation.
  • Con: More moving parts. If Communication fails, the patron doesn't get notified and doesn't know their hold is ready.

Decision: Option B. The extra complexity is worth it because notification channels will change over time, and Circulation shouldn't need to change when they do.

Should "patron has too many fines" block checkout?

A patron with $50 in unpaid fines tries to check out a book. Who blocks them?

Option A: Circulation checks with Finances before every checkout.

  • Pro: The block happens at the right moment
  • Con: Circulation is now coupled to Finances — it can't work without it

Option B: Finances notifies Accounts to suspend the patron. Circulation checks patron status (which it already does) and sees "suspended."

  • Pro: Circulation doesn't need to know about Finances at all. Account status is something it already checks.
  • Con: There's a window between the fine accruing and the account being suspended where the patron might check out another book.

Decision: Option B for most libraries (the window is acceptable). Option A if the rules are strict and the window is unacceptable (e.g., a very high-value collection).


What This Example Teaches

  1. Start with many nouns, end with few modules — 18 nouns became 6 modules
  2. Every boundary decision has a rationale — "Search is separate because..." not just "it felt right"
  3. The outside list prevents future mistakes — explicitly stating "Circulation does NOT handle fines" means nobody accidentally adds fine logic there
  4. Connections are minimal and directional — foundational modules have zero outbound dependencies
  5. Controversial decisions exist in every system — the mark of good design is making them deliberately and documenting why