Spring Boot 3 backend for a short-term rental platform, built with Domain-Driven Design, OAuth2 security, and production-grade patterns. Prioritises safety and comfort for female travellers.
┌──────────────────────────────────────────────────────────────┐
│ REST Clients │
└─────────────────────────┬────────────────────────────────────┘
│ HTTP/JSON
┌─────────────────────────▼────────────────────────────────────┐
│ Controller Layer │
│ BookingController · PropertyController · SearchController │
│ Auth0 JWT validation at entry │
└─────────────────────────┬────────────────────────────────────┘
│
┌─────────────────────────▼────────────────────────────────────┐
│ Service Layer │
│ BookingService · ReservationService · SearchService │
│ Business logic · transaction management │
└──────────┬───────────────────────────────┬───────────────────┘
│ │
┌──────────▼──────────┐ ┌────────────▼──────────────────┐
│ Repository Layer │ │ Auth Module │
│ JPA + PostgreSQL │ │ Auth0 + OAuth2 + RBAC │
└─────────────────────┘ └───────────────────────────────┘
Key design decisions:
- Domain-Driven Design (DDD) — codebase organised around Booking, Property, and User domains. Each domain owns its entities, services, and repositories. This keeps business logic from bleeding across boundaries and makes the system easier to scale independently.
- RBAC via Auth0 — travellers, landlords, and admins get different JWT claims. Role-checking is enforced at the service layer, not just the controller, so there's no path to escalate via internal calls.
- Search as a first-class concern — property search uses dynamic JPA Specifications so filters (location, dates, guests, amenities) compose cleanly without a combinatorial explosion of query methods.
- Separate booking and reservation flows — a
Bookingis a traveller's request; aReservationis a landlord's confirmed slot. Keeping them as separate aggregates avoids the stale-state bugs common in systems that conflate the two.
| Concern | Choice | Why |
|---|---|---|
| Framework | Spring Boot 3 + Java 21 | Virtual threads (Project Loom), records, sealed classes |
| Database | PostgreSQL (or MySQL) | ACID guarantees, rich query planner |
| ORM | Spring Data JPA + Hibernate | Specification-based dynamic queries |
| Auth | Auth0 + Spring Security OAuth2 | Delegated auth, JWT, RBAC without reimplementing |
| Build | Maven | Reproducible builds, standard CI compatibility |
| Containerisation | Docker Compose (compose.yaml) |
One-command local dev stack |
All endpoints are versioned under /api. Auth tokens are passed as Bearer in the Authorization header.
| Method | Endpoint | Role | Description |
|---|---|---|---|
POST |
/api/bookings |
TRAVELLER | Create a booking request |
GET |
/api/bookings/{id} |
TRAVELLER / LANDLORD | Get booking by ID |
DELETE |
/api/bookings/{id} |
TRAVELLER | Cancel a booking |
| Method | Endpoint | Role | Description |
|---|---|---|---|
GET |
/api/landlord/reservations |
LANDLORD | List all reservations |
PUT |
/api/landlord/reservations/{id} |
LANDLORD | Approve or reject |
DELETE |
/api/landlord/reservations/{id} |
LANDLORD | Remove reservation |
| Method | Endpoint | Role | Description |
|---|---|---|---|
GET |
/api/tenant/properties/search |
PUBLIC | Filter by location, dates, guests, amenities |
GET |
/api/tenant/properties/{id} |
PUBLIC | Get property details |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/auth/get-authenticated-user |
Returns decoded JWT claims |
src/main/java/com/example/quickstay/
├── booking/
│ ├── application/ # BookingService, ReservationService
│ ├── domain/ # Booking, Reservation entities
│ ├── infrastructure/ # JPA repositories
│ └── presentation/ # Controllers, DTOs, mappers
├── property/
│ ├── application/ # PropertyService, SearchService
│ ├── domain/ # Property, Picture, Amenity, etc.
│ ├── infrastructure/ # JPA repos + Specifications
│ └── presentation/ # Controllers, DTOs
├── user/
│ ├── application/ # UserService
│ ├── domain/ # Authority, ConnectedUser
│ └── presentation/ # AuthController
└── infrastructure/
└── config/ # SecurityConfig, Auth0 config
- JDK 21 (Adoptium Temurin)
- Maven (bundled via
./mvnw) - Docker (optional, for
compose.yamllocal stack) - Auth0 account (free tier works) — create one here
Create a .env file or export these before running:
AUTH0_CLIENT_ID=<your-auth0-client-id>
AUTH0_CLIENT_SECRET=<your-auth0-client-secret>
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/quickstay
SPRING_DATASOURCE_USERNAME=postgres
SPRING_DATASOURCE_PASSWORD=passworddocker-compose up -d # starts PostgreSQL
./mvnw spring-boot:run \
-Dspring-boot.run.arguments="--AUTH0_CLIENT_ID=$AUTH0_CLIENT_ID --AUTH0_CLIENT_SECRET=$AUTH0_CLIENT_SECRET"- Open project in IntelliJ IDEA
- Go to Run → Edit Configurations
- Add
AUTH0_CLIENT_IDandAUTH0_CLIENT_SECRETas environment variables - Click Run
The server starts at http://localhost:8080.
- GitHub Actions CI pipeline (build + test on every PR)
- Testcontainers-based integration tests against a real PostgreSQL instance
- Payment API integration (Stripe or Razorpay)
- Real-time booking notifications via WebSocket
- Property availability calendar endpoint
Built as a full backend engineering exercise applying Spring Boot 3, DDD, OAuth2 security, and JPA Specifications. The focus was on clean domain boundaries, role-based security enforced at the right layer, and a search implementation that doesn't fall apart when filters compose.