A high-performance reservation system built with Java 11 + Spring Boot 2.7.
This project demonstrates professional architecture, separation of concerns, and best practices in Spring Boot development.
- View available time slots
- Reserve a slot
- Cancel a reservation
- Retrieve reservation details
- Maintainable codebase (separation of concerns, testability)
- Clean, stable API layer (DTO-first, no entity leaks)
- Efficient queries (indexes and optimized schema, unique constraints)
- Robust concurrency-safe handling (pessimistic locking, safe reservations, no double bookings)
- Java 11
- Spring Boot 2.7
- Spring Data JPA
- H2 Database (in-memory for development/demo)
- JUnit 5 & Mockito (testing)
- DTO Mapping Layer for safe API responses
src/main/java/com/example/demo
├── controller # REST endpoints
├── service # Business logic
├── repository # Data access layer
├── model # Database entities
├── dto # API request/response objects
├── mapper # Entity <-> DTO mapping
├── exception # Centralized exception handling
├── config # Configuration (Swagger, DB)
└── util # Utility/helper classes
Architecture Layers:
Controller --> Service --> Repository --> Database
| |
| v
| Entity <-> DTO Mapper
v
Client API
-
Rationale
Entities (User,AvailableSlot,Reservation) represent database state and should not be exposed directly through controllers.
Using DTOs ensures a stable and clean API contract, independent from internal persistence details. -
Benefits
- Decouples API from JPA entities.
- Allows data shaping: only required fields are exposed.
- Prevents accidental lazy loading or persistence leaks.
-
Pagination Responses
Pagination endpoints returnPage<DTO>which include:content→ list of DTOstotalElements,totalPages,pageNumber,pageSize→ metadata useful for clients (e.g., infinite scroll or pagination UI).
- Problem: Multiple users may try to book the same slot at the same time.
- Solution:
- Database Constraints:
UNIQUE (slot_id)constraint on reservations prevents duplicate bookings. - Pessimistic Locking:
@Lock(LockModeType.PESSIMISTIC_WRITE)ensures that only one transaction can reserve a slot at a time.
- Database Constraints:
- Result: Double-bookings are prevented by both application-level locks and database-level integrity checks.
-
Why?
- Passing full entities between layers introduces coupling and may trigger unintended lazy loads.
- Using IDs is lightweight and forces proper transaction-bound entity lookups.
-
Advantages
- Clear separation between controller → service → repository layers.
- Easier testing and mocking (no entity state required).
- Ensures locking/caching is handled at the transaction boundary.
- Indexes
users(username),users(email)→ fast lookups for login/registration.available_slots(is_reserved, start_time)→ efficient filtering of available slots.reservations(user_id),reservations(slot_id)→ quick lookups for user/slot history.
- Constraints
NOT NULL+UNIQUE→ enforce schema correctness.FOREIGN KEY→ ensure referential integrity acrossusers,slots, andreservations.
- Unit Tests
- Services & repositories tested with
@DataJpaTest+ in-memory DB.
- Services & repositories tested with
- Integration Tests
- Full stack tests with
@SpringBootTest. - Shared
BaseIntegrationTestensures database is cleaned before each test run.
- Full stack tests with
- Web Layer Tests
- Controllers tested with
@WebMvcTestandMockMvc. - Dependencies mocked via
@MockBeanfor isolation.
- Controllers tested with
- Test Data Factory
- Centralized utility to create consistent, deterministic test entities and DTOs.
-
Build the project:
mvn clean install
-
Run the application:
mvn spring-boot:run
-
Access H2 Console (optional):
URL: http://localhost:8080/h2-console JDBC URL: jdbc:h2:mem:testdb Username: sa Password: (leave empty)
Request:
POST /api/reservations
Content-Type: application/json
{
"userId": 1,
"slotId": 10
}Response:
{
"id": 100,
"userId": 1,
"slotId": 10,
"reservedAt": "2025-09-11T09:00:00"
}Request:
GET /api/slots/available?page=0&limit=10Response:
{
"content": [
{
"id": 10,
"startTime": "2025-09-11T09:00:00",
"endTime": "2025-09-11T10:00:00",
"isReserved": false
}
],
"pageNumber": 0,
"pageSize": 10,
"totalElements": 5,
"totalPages": 1
}- JWT Authentication for secure API access
- Additional CRUD endpoints for slots and users
- Docker containerization
- Swagger/OpenAPI integration with pageable metadata