BookStore_FullStack: A Bookstore That Behaves Like a Real System

A Spring Boot and React case study in checkout state, JWT security, overdue fees, and testing against real PostgreSQL.

9 min read • View on GitHub • More from IliaMalafeev

A librarian's desk is rendered like a control room, with books, stamped cards, and ledger pipes connecting states such as available, checked out, overdue, and paid. It explains that the project is really about managing transitions, not just showing a catalog.
The bookstore is treated like a system of state changes. A book moves through a lifecycle, and the software keeps each transition honest.
Key Takeaways

A bookstore with a memory

Most bookstore apps are inventory shelves with a search bar. This one behaves more like a ledger with a storefront on top. A book can be available, checked out, returned, overdue, paid, and archived in history, and the repo is built around that sequence.

That is the useful surprise. The project is not just a showcase of Spring Boot and React. It is a deliberately stateful system that treats identity, payments, and support as part of the same domain, not as disconnected features.

The hidden state machine behind checkout

The core idea is procedural. A book does not just “rent.” It passes gates, updates records, and resolves into either a clean return or an overdue payment path.

The checkout flow is the heart of the repo. First, the system checks whether copies remain. Then it checks whether the user is allowed to borrow, including whether unpaid fees block the action. Only after both gates pass does it create the checkout record and decrement inventory.

That matters because the app is enforcing business rules at the boundary, not hoping the UI stays polite. The return flow follows the same logic. If a book comes back late, the system computes the overdue payment path and preserves the event in history.

A sealed book envelope passes through three mechanical gates marked by the visual logic of availability, eligibility, and payment. One path ends in a checkout card, while the other ends in an overdue notice. It explains that the workflow is conditional and stateful.
This is the part that makes the project feel real. The book only moves forward when each business rule clears the way.

Why the backend feels stricter than a tutorial

Full-stack developer from Russia.

Ilia Malafeev, Project Creator and Maintainer · IliaMalafeev (Ilia Malafeev) · GitHub

The backend is built like a system that expects failure and explains it clearly. JWT authentication runs statelessly, the security filter bridges errors into clean API responses, and role-based access keeps public, secure, and admin routes separate.

That is a small but important signal. Tutorials often stop at "it works." This repo goes farther and tries to make failure legible, which is what users and frontend developers actually need.

The data model does most of the work

ConcernTypical CRUD bookstoreBookStore_FullStack
Domain depthCatalog plus checkout buttonCatalog, rental lifecycle, overdue payment, support history
Auth modelLogin at the edgeStateless JWT with role-based access
Payment handlingOften omitted or mockedStripe-backed overdue fee handling
Error handlingGeneric 500s or loose validationCustom exceptions with specific API responses
Integration testingUnit tests or in-memory shortcutsTestcontainers against real PostgreSQL
Data integrityMostly application enforcedSQL-first schema with cascade behavior
Frontend/backend separationLoose or incidentalClear DTO and service boundaries

The schema is doing structural work, not just storing rows. Books and genres use many-to-many relationships. Person sits at the center of checkout, history, and discussion, which gives the app a single identity spine instead of scattered ownership.

That arrangement matters because the domain can grow without collapsing into spaghetti. Inventory, rentals, and support all stay attached to the same person and the same book objects, so the system keeps a coherent memory of what happened.

Testing like the database matters

Test strategyWhat it optimizes forWhat it risks
In-memory databaseFast feedbackBehavior that can drift from production
Real PostgreSQL with TestcontainersProduction-like confidenceMore setup and slower execution
Mock-heavy integrationIsolationFalse certainty around SQL and constraints

This is where the project looks more mature than most portfolio repos. Testcontainers means the app is tested against the database behavior it will actually see in production, not a convenient stand-in that quietly changes the rules.

That choice is boring in the best way. It says the author cares about how the system behaves when SQL constraints, transactions, and schema details matter. For a project like this, that is the difference between demo polish and engineering discipline.

A portfolio project with real operational instincts

SignalWhat it suggests
Swagger and OpenAPIThe API is meant to be consumed, not just admired
Stripe integrationPayments are treated as part of the domain, not decoration
Docker supportThe project is designed to be reproducible
DTO separation and ModelMapperEntities are not leaking straight into the UI
Monorepo splitFrontend and backend are intentionally decoupled

The result is a repo that reads like a rehearsal for production software. It is not trying to dazzle with novelty. It is trying to prove that a fairly ordinary domain can be built with uncommon discipline.