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.
- This repo stands out because it models a bookstore as a lifecycle system, where checkout, return, overdue fees, and history all belong to one coherent flow.
- Its backend choices, from JWT auth to granular exceptions, are aimed at precise behavior instead of generic CRUD responses.
- Testcontainers and real PostgreSQL testing signal that the project is trying to behave like production software, not a classroom demo.
- The data model does the heavy lifting, which lets the application stay consistent as the domain adds inventory, payments, and support.
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 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.
Why the backend feels stricter than a tutorial
Full-stack developer from Russia.
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
| Concern | Typical CRUD bookstore | BookStore_FullStack |
|---|---|---|
| Domain depth | Catalog plus checkout button | Catalog, rental lifecycle, overdue payment, support history |
| Auth model | Login at the edge | Stateless JWT with role-based access |
| Payment handling | Often omitted or mocked | Stripe-backed overdue fee handling |
| Error handling | Generic 500s or loose validation | Custom exceptions with specific API responses |
| Integration testing | Unit tests or in-memory shortcuts | Testcontainers against real PostgreSQL |
| Data integrity | Mostly application enforced | SQL-first schema with cascade behavior |
| Frontend/backend separation | Loose or incidental | Clear 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 strategy | What it optimizes for | What it risks |
|---|---|---|
| In-memory database | Fast feedback | Behavior that can drift from production |
| Real PostgreSQL with Testcontainers | Production-like confidence | More setup and slower execution |
| Mock-heavy integration | Isolation | False 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
| Signal | What it suggests |
|---|---|
| Swagger and OpenAPI | The API is meant to be consumed, not just admired |
| Stripe integration | Payments are treated as part of the domain, not decoration |
| Docker support | The project is designed to be reproducible |
| DTO separation and ModelMapper | Entities are not leaking straight into the UI |
| Monorepo split | Frontend 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.