Skip to content
<k/>
0%
Loading assets · 0s
<k/>
Loading...

Project

E-Library Management System

A library management app I built with Laravel to try out some pragmatic clean architecture ideas without overcomplicating things.

Shipped2026Featured
Stack
laravel-13php-8.5tailwind-cssmysqlpest-php
E-Library Management System

I originally developed the E-Library Management System as a university project, but I built it as a practical showcase for Pragmatic Clean Architecture in Laravel rather than a basic CRUD app.

My goal was to keep development fast while making the codebase maintainable long-term, and to make sure it holds up against common security issues and race conditions under concurrent load.

E-Library Dashboard Overview 1 E-Library Dashboard Overview 2

📸 User Interface (UI/UX)

I designed the system with a playful but professional theme, keeping the UI optimized for library staff who use it daily.

I set up a surface hierarchy for light and dark environments so contrast and readability stay consistent either way.

Light Mode Palette:

  • Background: Soft Slate Tint (#F8FAFC) with Crisp White Cards (#FFFFFF)
  • Primary: Bubblegum Pink (#EC4899)
  • Secondary: Sky Blue (#38BDF8)
  • Success Accent: Mint Green (#34D399)

Dark Mode Palette:

  • Background: Deep Slate (#0F172A) with Elevated Panels (#1E293B)
  • Primary: Soft Pink (#F472B6)
  • Secondary: Soft Blue (#60A5FA)
  • Success Accent: Mint Green (#34D399)

For typography, I chose Nunito Sans across both modes. It's rounded and readable, which fits the academic context well.

Book & Category Management

🚀 Core Features

I built the application to cover the full library workflow:

  1. Monitoring Dashboard: A dashboard showing key stats (overdue books, active borrow requests) with quick access to daily operational menus.
  2. Book & Category Management: I added support for book cover uploads with automatic cleanup, strict MIME type validation, and live stock tracking (total vs. available stock).
  3. Borrowing Circulation Flow: A borrowing system that supports multiple items per transaction, tied to specific member IDs.
  4. Flexible Returns & Dynamic Fines: I implemented partial returns (returning a fraction of borrowed items without resolving the whole ticket). Overdue fines are recalculated on each request based on daily system configurations.
  5. Automated Waitlist & Reservation System: When a borrowed book is returned, the circulation transaction checks for pending waitlists, locks the reservation record, and updates the next member's status to ready with a strict 48-hour expiration window.

Borrowing and Returning Circulation Partial Return Handling

🤖 Vibe Coding

This was the first project where I tried out "Vibe Coding" (AI-assisted development). Even though I let AI do a lot of the typing, I made sure not to skip the architecture and design work.

Before I wrote any code, I spent some time thinking about the architecture and edge cases. I documented the plan in the repository's .ai/ directory:

  • Requirement Analysis: Figuring out the edge cases in circulation logic so I don't get headaches later.
  • Security Threat Modeling: Designing defenses against double-returns and race conditions before writing any code.
  • Data Flow Design: Structuring database relationships and locking mechanisms.

AI helped me write code faster, but I still needed to understand the design before I started.

🏛️ Architecture & Code Principles

I applied my favorite "Thin Controllers, Fat Actions" philosophy to avoid spaghetti code in large MVC apps.

  • Thin Controllers: Controllers only receive HTTP Requests, delegate validation to FormRequest classes, and invoke the required Action class.
  • Isolated Action Classes: All complex business logic lives in Action classes (like BorrowBooksAction or ReturnBorrowingItemAction), making circulation logic reusable and easy to test in isolation. I prevent Double Returns by intercepting invalid states and throwing a DomainException before ever touching the database.
  • Dashboard Query Services: Dashboard metrics (return ratios, financial trends) are calculated with raw SQL CASE WHEN aggregations inside dedicated service classes (e.g., DashboardQueryService). This avoids loading full Eloquent collections into memory just to count things.
  • Dumb Blade Views: I built the presentation layer using dumb Blade templates combined with Tailwind CSS. No database queries or business logic in the presentation layer.
  • Dynamic Accessors: I calculate overdue days and fine amounts on the fly using Laravel Model Accessors (getLateDaysAttribute) rather than storing integers that can go stale in the database.

Database-Level Protection (Pessimistic Locking)

The trickiest part was preventing negative stock caused by race conditions. I handled it using database transactions and pessimistic locking:

// Example implementation within an Action Class
DB::transaction(function () use ($bookIds) {
    // lockForUpdate() prevents other requests from modifying these rows until my transaction completes
    $books = Book::whereIn('id', $bookIds)->lockForUpdate()->get();
    
    foreach ($books as $book) {
        if ($book->stock_available < 1) {
            throw new Exception("Stock for {$book->title} is empty.");
        }
        $book->decrement('stock_available');
    }
});

🔒 Security Posture & Hardening

Security was part of the design from the start, not added at the end. I covered the basics (preventing IDOR and privilege escalation) without over-engineering it:

  • Mass Assignment Protection: I always use $request->validated() from FormRequest instances so no unexpected fields can mutate my database.
  • Insecure File Upload Prevention: Extensions and MIME types are validated. Files are stored with random hash names to prevent execution exploits, and orphaned files are cleaned up via Laravel's Storage API.
  • Destructive Action Guards: Deletion endpoints (destroy()) check relationship constraints (e.g., $member->borrowings()->exists()) and return a clear error rather than exposing raw SQL exceptions to the user.
  • XSS & Enumeration Defense: Blade output is escaped by default, and I sanitize all wildcard search parameters (% and _) to prevent Slow Query Enumeration attacks.
  • HTTP Security Headers: Middleware enforces X-Frame-Options, X-Content-Type-Options, and Referrer-Policy.

🧪 Testing Strategy

I wrote tests for every main library flow using Pest PHP:

Pest PHP Testing Report 1 Pest PHP Testing Report 2

My test suite covers:

  • Feature Tests: All CRUD flows work as expected.
  • Business Logic Tests: Fine calculations are correct and stock can never drop below zero, regardless of the scenario.
  • Security Regression: Endpoints protected by role (Admin vs. Staff) can't be accessed by the wrong role.

Comments