Writing a full library system in plain Java, no frameworks
No Spring and no ORM. Borrowing, returns, fines and search were built from scratch for my Advanced Object Oriented Programming module, forcing me to design the domain model, rules and testing structure myself.
Role
Solo Engineer
Organisation
Self-directed
Industry
Software Architecture · Education
Focus
Java · Architecture · OOP
/ Problem
The goal was not simply to build a library application.
The module was designed to make us practise object oriented design, SOLID, DRY and design patterns without relying on frameworks to make the architectural decisions for us.
The system needed to manage books, DVDs, physical copies, members, borrowing, returns, overdue fines and search.
That created questions frameworks normally help answer.
Where should borrowing rules live? How should different media types behave? How do you calculate fines reliably? How do you test date-dependent behaviour when “today” changes every time the program runs?
The real challenge was keeping those rules flexible without turning the application into a collection of conditionals.
/ Solution
I separated titles from physical copies, so one media item can have several independently borrowable holdings.
Repository interfaces isolate persistence from the application logic, while LoanRule and FinePolicy strategies keep borrowing and fine rules replaceable.
One small abstraction became particularly useful.
Instead of calling the system date directly, overdue calculations use a ClockProvider. Production receives the real date, while tests can inject a fixed one.
That makes fines and due-date behaviour deterministic.
The project also includes catalogue search by title, creator and year.
Instead of adding JUnit, I wrote a small zero-dependency test runner with separate unit and functional suites covering validation, loan behaviour, search, holding states and complete borrowing workflows.

/ What I learned
This project made SOLID and design patterns feel practical rather than theoretical.
Their value appears when something changes.
A new media type should need a new rule, not a rewrite of the loan workflow. A change in storage should not affect the service layer. A date-dependent rule should not make testing unreliable.
I stopped thinking of patterns as structures that make code look well designed.
Their real purpose is giving future changes somewhere sensible to go.