Case Study: Modeling a Library System from CRC to UML
When translating CRC cards into UML, the real value isn’t in the syntax—it’s in preserving the intent behind the design. I’ve led dozens of modeling sessions where teams rushed to draw boxes and lines before fully understanding collaboration patterns. The result? Over-engineered models full of unnecessary associations and ambiguous responsibilities.
My advice? Let the CRC cards teach you. They’re not just a brainstorming aid—they’re a behavioral blueprint. In this CRC UML example project, we’ll walk through building a robust library system from scratch, using real CRC sample modeling to guide each decision in the class diagram.
By the end, you’ll know how to extract attributes, model associations with proper cardinality, and validate design completeness—all rooted in the natural language of team collaboration. This isn’t theory. It’s what I’ve used in production environments to align developers, analysts, and product owners around a single source of truth.
Step 1: Begin with CRC Cards – The Library System Brainstorm
I’ve worked with teams where the first step was sketching a single class called “Library.” That’s a red flag. It’s too early to name the system. Start with the roles users interact with.
During a joint workshop, we asked: Who or what handles the lending process? The answers came fast: Librarian, Book, Member, and Loan.
Each of these became a CRC card. Here’s a snapshot of the initial set:
- Class: Librarian
- Responsibilities: Issue a loan, Return a book, Check member eligibility
- Collaborators: Member, Book, Loan
- Class: Book
- Responsibilities: Track availability, Store ISBN, Update loan status
- Collaborators: Loan, Librarian
- Class: Member
- Responsibilities: Register, Borrow a book, Pay fines
- Collaborators: Librarian, Loan
- Class: Loan
- Responsibilities: Track due date, Record return status, Calculate fine
- Collaborators: Book, Member, Librarian
These aren’t just names—they’re design triggers. Every responsibility implies a method. Every collaboration implies a relationship.
Why This Order Matters
Observation: Most teams start with “Library” as a central class. But that’s a symptom of premature structuring. The domain should drive the model, not the system’s name.
Instead, I always ask: What are the key actors and artifacts? That question keeps us grounded in behavior, not hierarchy.
Step 2: Convert CRC Classes to UML Class Nodes
Now, translate each CRC card into a UML class. The class name becomes the class box.
Start with clear naming. Avoid “BookManager” or “MemberHandler.” The class should reflect the role, not the function. “Book” is better than “BookRepository” at this stage.
Here’s how the first few classes look in UML:
+------------------+
| Book |
+------------------+
| - title: String |
| - author: String |
| - isbn: String |
| - available: bool |
+------------------+
| + checkOut() |
| + returnBook() |
| + isAvailable() |
+------------------+
Notice: The attributes are derived from what the book holds. The methods come from its responsibilities.
When I first taught this process, a junior developer asked: Shouldn’t “due date” be in Book? That’s a sign we need to dig deeper. The due date belongs to the Loan, not the book.
Key Decision: What Belongs in Which Class?
Use this rule: Only data that directly belongs to the entity should be in its attributes.
So: Book holds ISBN, title, author, availability. But not due dates or fines.
That’s why the Loan class is critical. It owns the temporal behavior.
Step 3: Map Responsibilities to Methods and Attributes
Each CRC responsibility becomes a method or attribute in the class diagram.
For example:
- “Track availability” →
available: boolean(attribute) - “Update loan status” →
updateStatus(status: String)(method) - “Record return status” →
recordReturn()(method)
But not all responsibilities are methods. Some are state.
Example: “Check member eligibility” → this is a method, but it doesn’t belong in Librarian—it should be in Member as isEligible(): boolean.
This is where CRC sample modeling shines. The language of the card reveals intent. “Check” implies a query. “Issue” implies an action.
Patterns to Watch
- Verbs like “issue”, “return”, “check” → usually operations
- Nouns like “availability”, “due date” → usually attributes
- Questions like “is eligible?” → boolean-returning methods
These patterns help you avoid overloading classes with unnecessary methods.
Step 4: Model Collaborations as UML Associations
Now, turn collaborations into associations. Each collaboration in a CRC card maps to a line in the UML diagram.
For example, “Librarian collaborates with Member, Book, Loan” becomes:
- Librarian —— Member (1 to many)
- Librarian —— Book (1 to many)
- Librarian —— Loan (1 to many)
But wait—this is too simplistic. Let’s look deeper.
Who creates a Loan? The Librarian. So, the association should be directed from Librarian to Loan.
Now, who owns the Loan? The system. But the Member is the one who initiates the loan. So, Member creates a Loan, and the Librarian approves it.
Here’s how we refine the associations:
| Association | Cardinality | Direction | Justification |
|---|---|---|---|
| Member —— Loan | 1 to many | Member → Loan | Member creates multiple loans |
| Book —— Loan | 1 to many | Loan → Book | A loan is for one book |
| Librarian —— Loan | 1 to many | Librarian → Loan | Librarian issues loans |
Notice the direction matters. The arrow shows who controls the relationship.
Why Navigation Matters
Always ask: Can I navigate from A to B? If yes, draw the arrow.
For example: From Loan, can you go to Book? Yes, because a loan refers to a book.
From Book to Loan? Only if you’re tracking active loans. But a book doesn’t own the loan—it’s referenced.
So: Loan → Book is correct. The reverse is optional, unless you have a getLoans() method in Book.
Step 5: Add Attributes and Constraints
Now, identify attributes that represent state.
For Loan:
dueDate: DatereturnDate: Datestatus: String(e.g., “active”, “returned”, “overdue”)fineAmount: double
These aren’t just random fields. They emerge from responsibilities: “Calculate fine” needs a fineAmount attribute.
Also, add constraints where needed:
dueDate > currentDatestatus ∈ {“active”, “returned”, “overdue”}
Constraints clarify behavior. They’re the guardrails of the model.
Step 6: Identify Inheritance and Interfaces
Now, ask: Do any classes share common behavior?
For example, both Member and Librarian can “check” something. But “check eligibility” and “check availability” are different.
But what if we introduce Person as a superclass?
- Person → Member, Librarian
- Attributes:
name: String,address: String - Methods:
getName(): String,getAddress(): String
But stop. Is “Person” the right abstraction?
Yes—but only if both have shared identity, address, and name. If not, avoid forcing inheritance.
Instead, extract a Person class only if it adds clarity. Otherwise, keep them separate.
When to Use Inheritance
- When two classes share >70% of behavior and state
- When you can define a is-a relationship
- When you need polymorphic behavior
For a library system, Member and Librarian are roles, not types. So inheritance isn’t needed.
Step 7: Validate the Model
Now, step back. Is the model complete? Cohesive? Free of ambiguity?
Use this checklist:
- Every CRC responsibility is mapped to a method or attribute
- Every collaboration is represented as an association with correct cardinality
- Classes have clear, distinct responsibilities
- No class has more than 5–7 methods
- Associations are navigable only where needed
If any of these fail, go back to the CRC cards and re-clarify.
I once worked on a library system where “Loan” had 15 methods. The team didn’t realize it was handling payment, return, fine, and status tracking—too many responsibilities. The fix? Split into Loan and FineCalculator.
Frequently Asked Questions
How do I know when my CRC UML example project is ready?
When every responsibility is accounted for, every collaboration is linked, and no class has more than five methods. Ask: Can this class be understood without reading the code? If yes, you’re good.
Why use CRC sample modeling instead of jumping straight to UML?
Because CRC cards are language-driven. They reflect how teams think. UML formalizes that. Skipping CRC leads to models that are technically correct but miss behavioral intent.
Can I use this library system UML for software development?
Absolutely. This model directly maps to real classes in Java, Python, or C#. The attributes become fields, methods become functions, and associations become relationships in your database or ORM.
What if two people have different interpretations of a CRC card?
That’s expected. Use the CRC cards as discussion prompts. The goal isn’t consensus—it’s to surface ambiguity. Then refine the model together.
How do I handle time-based behavior like due dates?
Model it in the class that controls it. Loan manages the due date. Use a calculateFine() method that compares returnDate and dueDate.
Should I include the Library class in the diagram?
Only if it has behavior. If it’s a container, consider it an aggregate root. But avoid over-engineering—it can be a collection of Loan objects.