Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SeatFlow

SeatFlow is a production-style Cinema Seat Reservation System backend built with Java 21 and Spring Boot 3. The core engineering problem is safe concurrent seat reservation: two users must never be able to reserve the same seat for the same showtime.

The project is intentionally a layered monolith, not a microservice demo. It focuses on clean backend architecture, transactional business rules, PostgreSQL constraints, JWT security, Dockerized local setup, and tests around the critical reservation flow.

Tech Stack

  • Java 21
  • Spring Boot 3
  • Maven
  • PostgreSQL
  • Spring Data JPA
  • Spring Security + JWT
  • Flyway migrations
  • Redis-backed Spring Cache
  • Swagger/OpenAPI
  • JUnit 5, Mockito, Testcontainers
  • Docker + Docker Compose
  • Lombok

Architecture

src/main/java/com/seatflow
├── config        # Security, OpenAPI, application properties, bootstrap config
├── controller    # REST endpoints and HTTP response codes
├── dto           # Request/response contracts
├── entity        # JPA persistence model
├── exception     # Global exception handling and domain exceptions
├── mapper        # Entity-to-DTO conversion
├── repository    # Spring Data JPA access
├── security      # JWT, principal, user details, auth filter
├── service       # Transactional business logic
├── util          # Small reusable helpers
└── validation    # Custom Bean Validation constraints

Reservation Concurrency Strategy

Seat reservation is protected by two layers:

  1. ReservationService.reserve(...) runs in a transaction and locks requested Seat rows with PESSIMISTIC_WRITE.
  2. PostgreSQL enforces a partial unique index:
create unique index ux_active_showtime_seat
    on reservation_seats(showtime_id, seat_id)
    where active = true;

Pending and confirmed reservations keep reservation_seats.active = true. Expired or cancelled reservations release their seats by setting active = false. This means the database rejects double-booking even if two application requests race.

Features

  • User registration and login
  • BCrypt password hashing
  • JWT authentication
  • USER and ADMIN roles
  • Movie management
  • Cinema room creation with generated seat layout
  • Showtime scheduling with overlap prevention
  • Seat reservation with expiration holds
  • Mock payment approval/failure flow
  • Admin reservation search
  • Admin occupancy statistics
  • Pagination, filtering, sorting, validation, and standardized API responses
  • Swagger UI
  • Dockerized PostgreSQL, Redis, and API service

Quick Start

Create a local environment file:

cp .env.example .env

Start the full stack:

docker-compose up --build

The API will be available at:

  • API base URL: http://localhost:8080/api/v1
  • Swagger UI: http://localhost:8080/swagger-ui.html
  • Health check: http://localhost:8080/actuator/health

The Docker Compose setup bootstraps an admin account from environment variables:

ADMIN_EMAIL=admin@seatflow.local
ADMIN_PASSWORD=change-this-admin-password

Change these values before using the project outside local development.

Running Locally Without Docker

Start PostgreSQL and Redis, then provide the same variables from .env.example.

mvn spring-boot:run

Run the test suite:

mvn test

The integration test uses Testcontainers and requires Docker.

Example API Flow

Register a user:

curl -X POST http://localhost:8080/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "fullName": "Ada Lovelace",
    "email": "ada@example.com",
    "password": "strong-password"
  }'

Login:

curl -X POST http://localhost:8080/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "ada@example.com",
    "password": "strong-password"
  }'

Create a movie as admin:

curl -X POST http://localhost:8080/api/v1/movies \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Interstellar",
    "genre": "Sci-Fi",
    "durationMinutes": 169,
    "rating": "PG-13",
    "synopsis": "A team travels through a wormhole in search of a future for humanity.",
    "active": true
  }'

Create a room with generated seats:

curl -X POST http://localhost:8080/api/v1/rooms \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Room A",
    "rowsCount": 8,
    "seatsPerRow": 12
  }'

Create a showtime:

curl -X POST http://localhost:8080/api/v1/showtimes \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "movieId": 1,
    "roomId": 1,
    "startsAt": "2026-09-01T20:00:00Z",
    "ticketPrice": 12.50
  }'

Reserve seats:

curl -X POST http://localhost:8080/api/v1/reservations \
  -H "Authorization: Bearer <USER_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "showtimeId": 1,
    "seatIds": [1, 2, 3]
  }'

Pay for a reservation:

curl -X POST http://localhost:8080/api/v1/reservations/1/payments \
  -H "Authorization: Bearer <USER_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentToken": "tok_mock_approved",
    "simulateFailure": false
  }'

View occupancy statistics as admin:

curl "http://localhost:8080/api/v1/admin/occupancy?showtimeId=1" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"

API Design Notes

  • All request bodies are validated with Bean Validation.
  • Responses use a consistent ApiResponse<T> wrapper.
  • Validation errors include field-level details.
  • List endpoints support Spring pageable parameters such as page, size, and sort.
  • Business conflicts return 409 Conflict, missing resources return 404 Not Found, and unauthorized requests return 401 Unauthorized.

Database Design

Flyway owns schema creation in src/main/resources/db/migration.

Important modeling choices:

  • Users and roles are normalized through user_roles.
  • Seats belong to a cinema room and are generated when a room is created.
  • Showtimes reference a movie and room, with an overlap check in the service layer.
  • Reservations own reservation-seat rows.
  • Active reservation seats are protected by a PostgreSQL partial unique index.
  • Payments are one-to-one with reservations.

Testing

The project includes:

  • Mockito service tests for payment edge cases.
  • A PostgreSQL/Testcontainers integration test that launches concurrent reservation attempts for the same seat and verifies only one succeeds.
mvn test

Screenshots

Add screenshots here after running locally:

  • Swagger UI
  • Successful login response
  • Seat reservation response
  • Admin occupancy statistics
  • Test run output

Future Improvements

  • Add refresh tokens and token revocation.
  • Add cancellation endpoint with refund policy simulation.
  • Add richer reporting by movie, room, and date range.
  • Add rate limiting for authentication endpoints.

Guia paso a paso para ejecutar el programa

Esta guia asume que vas a ejecutar SeatFlow con Docker, que es la forma recomendada porque levanta la API, PostgreSQL y Redis con un solo comando.

1. Requisitos previos

Instala y abre Docker Desktop:

  • Docker Desktop para Windows, macOS o Linux.
  • Git, si vas a clonar el repositorio.

Verifica que Docker este funcionando:

docker --version
docker compose version

2. Entra a la carpeta del proyecto

En Windows PowerShell:

cd C:\Users\emica\Documents\Practicas\Web\SeatFlow

Si clonaste el repositorio en otra ruta, entra a la carpeta donde lo descargaste.

3. Crea el archivo de variables de entorno

Copia el archivo de ejemplo:

Copy-Item .env.example .env

En Linux/macOS:

cp .env.example .env

Puedes dejar los valores por defecto para desarrollo local. Antes de usar el proyecto en un entorno real, cambia especialmente:

JWT_SECRET
POSTGRES_PASSWORD
ADMIN_PASSWORD

4. Levanta toda la aplicacion

Ejecuta:

docker compose up --build

La primera vez puede tardar porque Docker debe descargar imagenes y Maven debe descargar dependencias.

Cuando el servicio termine de iniciar, la API estara disponible en:

http://localhost:8080

5. Abre Swagger

En el navegador abre:

http://localhost:8080/swagger-ui.html

Desde Swagger puedes probar todos los endpoints sin escribir curl manualmente.

6. Inicia sesion como administrador

Usa el endpoint:

POST /api/v1/auth/login

Con las credenciales configuradas en .env:

{
  "email": "admin@seatflow.local",
  "password": "change-this-admin-password"
}

Copia el valor de accessToken de la respuesta.

7. Autoriza Swagger con el JWT

En Swagger, presiona el boton Authorize y escribe:

Bearer TU_ACCESS_TOKEN

Reemplaza TU_ACCESS_TOKEN por el token real.

8. Prueba el flujo principal

Como administrador:

  1. Crea una pelicula con POST /api/v1/movies.
  2. Crea una sala con POST /api/v1/rooms.
  3. Crea una funcion con POST /api/v1/showtimes.

Como usuario:

  1. Registra un usuario con POST /api/v1/auth/register.
  2. Inicia sesion con POST /api/v1/auth/login.
  3. Reserva asientos con POST /api/v1/reservations.
  4. Paga la reserva con POST /api/v1/reservations/{reservationId}/payments.

9. Detener la aplicacion

Presiona Ctrl + C en la terminal donde esta corriendo Docker Compose.

Para detener y eliminar los contenedores:

docker compose down

Para eliminar tambien los datos de PostgreSQL guardados en el volumen local:

docker compose down -v

10. Ejecutar pruebas

Si tienes Maven instalado:

mvn test

Si no tienes Maven instalado, puedes usar Docker:

docker run --rm -v "$(pwd):/workspace" -w /workspace maven:3.9.9-eclipse-temurin-21 mvn test

En Windows PowerShell:

docker run --rm -v "${PWD}:/workspace" -w /workspace maven:3.9.9-eclipse-temurin-21 mvn test

Las pruebas de integracion usan Testcontainers, asi que Docker debe estar abierto.

Problemas comunes

Si el puerto 8080 ya esta ocupado, cambia el mapeo del servicio app en docker-compose.yml.

Si ves un error relacionado con PKIX path building failed, normalmente es un problema de certificados o proxy al descargar dependencias de Maven desde Docker.

Si PostgreSQL no inicia correctamente, reinicia los contenedores:

docker compose down
docker compose up --build

About

Production-style cinema reservation backend built with Java 21, Spring Boot 3, PostgreSQL, JWT authentication, Docker, and concurrent seat booking protection.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages