Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JavaBookStore

A RESTful bookstore backend structured using Clean Architecture principles. built with Java 25, Spring Boot 4, and PostgreSQL 18. Designed as a portfolio project demonstrating clean architecture principles, JWT-based authentication, and containerized deployment via Docker.


Features

  • JWT authentication
  • Role-based authorization (CUSTOMER - ADMIN)
  • Book CRUD operations
  • Pagination
  • Rate limiting
  • BCrypt password hashing
  • RFC 9457 error responses
  • Docker deployment

Project Structure

src/main/java/com/bookstore/javabookstore/
├── domain/
│ ├── exception/
│ ├── model/
│ ├── repository/
│ └── service/
├── application/
│ └── usecase/
│ ├── book/
│ └── user/
├── infrastructure/
│ ├── config/
│ ├── persistence/
│ │ ├── entity/
│ │ ├── mapper/
│ │ └── repository/
│ └── security/
└── presentation/
├── controller/
└── dto/
├── request/
└── response/

Architecture

The project enforces clean architecture with four layers. Dependencies flow inward; infrastructure depends on application, application depends on domain, domain depends on nothing outside itself and is the main foundation.

Layer Responsibility
domain Business models and rules. No framework dependencies. Just pure Java.
application Use cases. Orchestrates domain objects. No HTTP, no JPA.
infrastructure Framework and technology concerns and implementations.
presentation HTTP boundary.
Package Description
domain.model Book, User (rich domain objects with validation)
domain.repository Repository interfaces (contracts, not implementations)
domain.service Service interfaces (PasswordHasher, TokenService, LoginAttemptService)
domain.exception Domain exceptions mapped to HTTP status codes
application.book AddBookUseCase, GetBooksUseCase, UpdateBookUseCase, DeleteBookUseCase.
application.user RegisterUserUseCase, LoginUseCase.
infrastructure.config Bean wiring for use cases.
infrastructure.persistence JPA entities, Spring Data repositories, mappers.
infrastructure.security JWT, Bcrypt, rate limiting, Spring Security config.
presentation.controller AuthController, BookController.
presentation.dto Request and response records.
presentation.GlobalExceptionHandler Maps domain exceptions to RFC 9457 ProblemDetail responses.

Tech Stack

Technology Stack
Language Java 25
Framework Spring Boot 4 / Spring Framework 7
Security Spring Security , jjwt
Persistence Spring Data JPA, Hibernate, PostgreSQL 18
Password hashing BCrypt
Rate limiting Bucket4j + Caffeine cache
Containerization Docker, Docker Compose
Version Control Git

Security Design

Authentication uses stateless JWT (HS256). Tokens are issued on login and contain userId, email, username, and role claims. No server-side session is maintained.

Password handling: raw passwords never enter the domain layer; this keeps cryptographic concerns outside the business domain. The infrastructure layer hashes with Bcrypt before passing the hash to User.create().

Rate limiting:

  • Signup: 10 requests per IP per 2 hours (Caffeine Cache-backed per-IP bucket, evicts after 2 hours of inactivity, max 10,000 tracked IPs)
  • Login: 10 failed attempts per email triggers a 2-hour lockout (in-memory, resets on server restart, doesn't allow actual user to get punished)
  • Bucket4j implements the Token Bucket algorithm for request throttling

Design tradeoffs:

  • JWT logout is client-side only (token deletion). Server-side invalidation would require a denylist (Redis), which is out of scope.
  • Login lockout resets on server restart. Persistent lockout would require a DB-backed store like Redis.
  • ddl-auto=update is used for development convenience. Production should use Flyway migrations.
  • Role-based route enforcement is not yet implemented at the API level.

API Reference

All endpoints return application/json. Errors follow RFC 9457 ProblemDetail format to provide detailed error breakdown.

Auth -- /api/auth

POST /api/auth/register Register a new user
POST /api/auth/login Login and receive JWT

Register request: Request Body

{
  "username": "user",
  "email": "username@example.com",
  "password": "securepassword"
}

Register response (201): Response

{
  "id": "uuid",
  "username": "user",
  "email": "username@example.com",
  "role": "CUSTOMER"
}

Login request: Request Body

{
  "email": "username@example.com",
  "password": "securepassword"
}

Login response (200): Response

{
  "token": "abXY..."
}

Books -- /api/books

GET /api/books?page=0&pageSize=10 List books (paginated format)
POST /api/books Add a book
PUT /api/books/{id} Update a book
DELETE /api/books/{id} Delete a book

Create/Update request: Request Body

{
  "name": "Clean Code",
  "author": "Robert C. Martin",
  "pages": 431,
  "description": "A handbook of agile software craftsmanship."
}

Book response: Response

{
  "id": "uuid",
  "name": "Clean Code",
  "author": "Robert C. Martin",
  "pages": 431,
  "description": "A handbook of agile software craftsmanship."
}

Error responses (RFC 9457 ProblemDetail)

Status Thrown when
400 - Bad Request Validation failure
401 - Unauthorized Invalid credentials or locked account
404 - Not Found Book ID does not exist
409 - Conflict Email or username already registered
429 - Too Many Requests Signup rate limit exceeded
500 - Internal Server Error Unexpected failure

Running Locally

Prerequisites: Docker, Docker Compose.

  1. Clone the repository
  2. Create .env in the project root:
    DB_PASSWORD=your_password
    JWT_SECRET=<base64-encoded 32-byte key — generate with: openssl rand -base64 32>
  3. Run:
docker compose up --build
  1. API is available at http://localhost:8085

Promoting a user to ADMIN (admin must be set manually):

UPDATE users SET role = 'ADMIN' WHERE email = 'your@email.com';

Running Without Docker

  1. Ensure PostgreSQL is running locally on port 5432 with a bookstore database
  2. Copy application.properties.example to src/main/resources/application.properties and fill in values
  3. Run: ./mvnw spring-boot:run

Kindly refer to application.properties.example inside src/main/java/com/bookstore/javabookstore/ for a premade key file.


About

An entire CRUD bookstore System made using Java 25, and Spring Boot.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages