Diseño y Desarrollo de una Plataforma de Venta de Boletos de Cine con Spring Boot

Arquitectura Tecnológica del Sistema

La implementación de un sistema de venta de boletos para cine se sustenta en una pila tecnológica robusta, orientada a la escalabilidad y mantenibilidad.

Componentes del Lado del Servidor (Backend)

  • Framework Principal: Spring Boot 2.x, para una configuración ágil y desarrollo rápido de microservicios.
  • Capa de Persistencia: MyBatis-Plus o Spring Data JPA para la interacción eficiente con la base de datos.
  • Gestión de Datos:
    • MySQL 8.0 como base de datos relacional principal.
    • Redis para caching de datos frecuentes y la gestión de la concurrencia en procesos críticos como la venta de boletos.
  • Seguridad: Spring Security en combinación con JWT (JSON Web Tokens) para autenticación y autorización segura de usuarios.
  • Middleware:
    • RabbitMQ, actuando como broker de mensajes para operaciones asíncronas y desacoplamiento de servicios.
    • Elasticsearch, optimizado para la funcionalidad de búsqueda de contenido y películas.
  • Utilidades: Lombok para reducir el boilerplate, Swagger para documentación interactiva de APIs y Logback para la gestión de logs.

Componentes del Lado del Cliente (Frontend - Opcional Arquitectura Separada)

  • Aplicación Web: Thymeleaf (para renderizado del lado del servidor) o un enfoque con Vue.js y la biblioteca de componentes Element UI.
  • Aplicaciones Móviles: Uniapp o desarrollo nativo para WeChat Mini Programs.

Infraestructura y Despliegue

  • Contenedorización: Docker y Docker Compose para empaquetar y orquestar los servicios de la aplicación.
  • CI/CD (Integración y Entrega Continua): Jenkins o GitHub Actions para automatizar los flujos de compilación, prueba y despliegue.

Funcionalidades Clave del Sistema

La plataforma ofrece un conjunto completo de características para usuarios y administradores, estructuradas en módulos principales:

Módulo de Usuarios y Cuentas

  • Proceso de registro y autenticación (usando número de teléfono con SMS OTP o tokens JWT).
  • Gestión de perfil (actualización de datos personales, cambio de contraseña, carga de avatares).
  • Sistema de fidelización por niveles de membresía, con acumulación de puntos canjeables por beneficios.

Módulo de Gestión de Películas

  • Clasificación de títulos (estrenos, próximas, mejer valoradas).
  • Páginas de detalle de película (ficha técnica, elenco, tráileres, calificaciones y reseñas).
  • Búsqueda avanzada de películas utilizando Elasticsearch para resultados rápidos y precisso.

Módulo de Venta de Boletos

  • Consulta de sesiones y horarios disponibles en cines, mostrando el estado actual de los asientos.
  • Proceso de selección de asientos y compra, con protección contra sobreventa mediante bloqueos distribuidos de Redis.
  • Gestión de historial de pedidos, incluyendo la cancelación automática de reservas no pagadas a tiempo.

Módulo de Pagos

  • Integración con pasarelas de pago (ej. Alipay/WeChat Pay en modo sandbox).
  • Funcionalidad para procesamiento de reembolsos (parciales o totales).

Módulo de Administración del Sistema

  • Herramientas CRUD (Crear, Leer, Actualizar, Eliminar) para la gestión de cines y la configuración de horarios de proyección.
  • Generación de informes y estadísticas de ventas de boletos con visualizaciones (ej. ECharts).
  • Sistema de auditoría de logs, registrando opearciones críticas mediante Aspect-Oriented Programming (AOP).

Esquema de Base de Datos (Tablas Principales)

A continuación, se presenta el diseño lógico de las tablas esenciales para el funcionamiento de la plataforma.


-- Tabla de Clientes
CREATE TABLE `clientes` (
  `id_cliente` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `nombre_acceso` VARCHAR(50) UNIQUE NOT NULL,
  `hash_contrasena` VARCHAR(255) NOT NULL, -- Mayor longitud para contraseñas hasheadas
  `email_contacto` VARCHAR(100) UNIQUE,
  `telefono_movil` VARCHAR(15) UNIQUE,
  `url_imagen_perfil` VARCHAR(255),
  `puntos_fidelidad` INT DEFAULT 0
);

-- Tabla de Contenido Fílmico
CREATE TABLE `titulos_cine` (
  `id_titulo` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `nombre_pelicula` VARCHAR(150) NOT NULL,
  `director_principal` VARCHAR(80),
  `calificacion_promedio` DECIMAL(2,1), -- Calificación sobre 10
  `duracion_minutos` INT COMMENT 'Duración en minutos',
  `url_poster` VARCHAR(255),
  `genero` VARCHAR(50)
);

-- Tabla de Proyecciones (Sesiones de Cine)
CREATE TABLE `sesiones_cine` (
  `id_sesion` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `id_titulo` BIGINT NOT NULL,
  `id_sala` BIGINT NOT NULL, -- Asumiendo una tabla 'salas' o 'cines'
  `hora_inicio_proyeccion` DATETIME NOT NULL,
  `precio_unitario` DECIMAL(10,2) NOT NULL,
  `asientos_disponibles` INT,
  FOREIGN KEY (`id_titulo`) REFERENCES `titulos_cine`(`id_titulo`)
);

-- Tabla de Reservas/Órdenes
CREATE TABLE `reservas_boletos` (
  `id_reserva` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `id_cliente` BIGINT NOT NULL,
  `id_sesion` BIGINT NOT NULL,
  `lista_asientos_reservados` VARCHAR(255) COMMENT 'Ej: A1, B3, C5',
  `estado_reserva` TINYINT COMMENT '0: Pendiente Pago, 1: Confirmada, 2: Cancelada',
  `fecha_creacion` DATETIME DEFAULT CURRENT_TIMESTAMP,
  `fecha_actualizacion` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  FOREIGN KEY (`id_cliente`) REFERENCES `clientes`(`id_cliente`),
  FOREIGN KEY (`id_sesion`) REFERENCES `sesiones_cine`(`id_sesion`)
);

Estrategias de Prueba del Sistema

Para asegurar la calidad y fiabilidad de la plataforma, se proponen las siguientes metodologías de prueba:

Pruebas Unitarias

Empleo de JUnit 5 y Mockito para la validación de la lógica de negocio en la capa de servicios, aislando componentes.

Ejemplo de prueba de concurrencia en la reserva de boletos:


import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@SpringBootTest // O usar @ExtendWith(MockitoExtension.class) para un alcance más específico
class PruebaServicioReservasConcurrencia {

    @Mock
    private RepositorioSesiones repositorioSesiones; // Mock para la interacción con la base de datos de sesiones

    @InjectMocks
    private ServicioGestorBoletos servicioGestorBoletos; // El servicio que queremos probar

    @Test
    void verificarReservasEnEscenarioConcurrente() throws InterruptedException {
        final Long ID_SESION_PELICULA = 101L;
        final int ASIENTOS_INICIALES = 1; // Suponemos que inicialmente hay 1 asiento disponible
        final int NUM_INTENTOS_CONCURRENTES = 50; // Múltiples usuarios intentan reservar

        // Simula el estado inicial de la sesión: 1 asiento disponible.
        // Utilizamos un AtomicInteger para modelar el decremento concurrente de asientos.
        AtomicInteger asientosDisponiblesSimulados = new AtomicInteger(ASIENTOS_INICIALES);

        // Configura el mock para simular la obtención del total de asientos disponibles.
        Mockito.when(repositorioSesiones.obtenerTotalAsientosDisponibles(ID_SESION_PELICULA))
               .thenAnswer(invocation -> asientosDisponiblesSimulados.get()); // Retorna el valor actual

        // Simula la lógica de intentar reservar un asiento: si hay disponibilidad, se decrementa.
        Mockito.doAnswer(invocation -> {
            if (asientosDisponiblesSimulados.get() > 0) {
                asientosDisponiblesSimulados.decrementAndGet();
                return true; // Simula una reserva exitosa
            }
            return false; // Simula una reserva fallida por falta de asientos
        }).when(repositorioSesiones).intentarReservarUnAsiento(ID_SESION_PELICULA);


        ExecutorService ejecutorHilos = Executors.newFixedThreadPool(NUM_INTENTOS_CONCURRENTES);
        CountDownLatch compuertaSincronizacion = new CountDownLatch(NUM_INTENTOS_CONCURRENTES);

        for (int i = 0; i < NUM_INTENTOS_CONCURRENTES; i++) {
            final int clienteId = i + 1;
            ejecutorHilos.submit(() -> {
                try {
                    // Cada "cliente" intenta reservar un boleto para la misma sesión.
                    // Se asume un método en el servicio que gestiona la compra.
                    servicioGestorBoletos.procesarCompraDeBoleto(ID_SESION_PELICULA, (long) clienteId);
                } catch (Exception ignored) {
                    // Las excepciones por falta de asientos son esperadas y manejadas internamente por el servicio.
                } finally {
                    compuertaSincronizacion.countDown();
                }
            });
        }

        compuertaSincronizacion.await(10, TimeUnit.SECONDS); // Espera hasta que todos los hilos terminen
        ejecutorHilos.shutdown(); // Apaga el pool de hilos

        // Verifica que, a pesar de los múltiples intentos, solo una reserva fue exitosa
        // y el conteo de asientos disponibles simulados es 0.
        Mockito.verify(repositorioSesiones, Mockito.times(1))
               .intentarReservarUnAsiento(ID_SESION_PELICULA);

        Assertions.assertEquals(0, asientosDisponiblesSimulados.get(),
                                "Debería quedar 0 asientos disponibles después de la reserva concurrente del único asiento.");
    }
}

Pruebas de Integración

  • Utilización de Postman o herramientas similares para validar la funcionalidad completa de las APIs, incluyendo escenarios de éxito y manejo de errores (ej. intentos de compra duplicados).
  • Uso de Testcontainers para entornos de prueba aislados con bases de datos como MySQL en contenedores Docker.

Pruebas de Carga

  • Realización de pruebas de estrés con herramientas como JMeter para simular volúmenes altos de transacciones (ej. >500 QPS en picos de venta de boletos).
  • Monitoreo del rendimiento de Redis (tasa de aciertos de caché) y análisis de consultas lentas en MySQL para optimización.

Estructura de Directorios del Código Fuente

La organización del proyecto sigue una estructura modular estándar para aplicaciones Spring Boot:


src/
├── main/
│   ├── java/com/aplicacion/cine/
│   │   ├── configuracion/  # Clases para configuración general (seguridad, Redis, etc.)
│   │   ├── controladores/  # Puntos de entrada para las peticiones HTTP (APIs REST)
│   │   ├── servicios/      # Implementación de la lógica de negocio principal
│   │   ├── repositorios/   # Interfaces de acceso a datos (ej. Spring Data JPA, MyBatis)
│   │   └── modelos/        # Clases de entidades de dominio y DTOs
│   └── resources/
│       ├── application.yml # Ficheros de propiedades y configuración por entorno
│       └── mapper/         # (Si se usa MyBatis) Ficheros XML de mapeo SQL
└── test/                   # Contiene todas las pruebas unitarias y de integración

Etiquetas: Spring Boot MySQL Redis RabbitMQ Elasticsearch

Publicado el 8-12 16:29