Eventify
Eventify es una plataforma full-stack en Laravel para crear, promocionar y gestionar eventos de cualquier escala — desde una reunión pequeña hasta un concierto agotado — con inventario real de tickets, sedes geolocalizadas y recibos en PDF, así el organizador no necesita un servicio de ticketing aparte pegado a una página de listado.
Arquitectura y Stack Tecnológico
Arquitectura Central
- Backend: Laravel 10 (PHP), dashboard renderizado en servidor + una API REST
- Frontend: vistas Blade + React para las piezas interactivas, Bootstrap 5 para el estilo
- Base de datos: MySQL, seis tablas relacionadas (
users,roles,events,tickets,categories,statuses,locations), todas con soft-delete víadeleted_at - Geocodificación: OpenStreetMap Nominatim — convierte una dirección en
latitude/longitudeen la tablalocations - Generación de PDF: DomPDF — transmite reportes de tickets y eventos bajo demanda
- Autorización: un middleware de rol personalizado (
role:admin|user), sin clases de política ni bit-masks de permisos
Arquitectura en Capas
Ambos roles comparten el mismo dashboard y los mismos flujos de eventos/tickets; solo las rutas exclusivas de admin (gestión de usuarios, categorías, estados) están protegidas por el middleware de rol. Las rutas públicas /explore y /events/{id}/report no requieren autenticación en absoluto.
Flujo de una Petición
Crear un evento hace cuatro cosas en una sola petición: geocodificar la dirección, guardar la imagen, asignar un estado Disponible/Agotado a partir de la capacidad, y persistir — todo antes de que el organizador vea una respuesta.
Características Principales
Características de un Vistazo
Creación de Eventos con Geocodificación en Vivo
Enviar el formulario del evento (POST /dashboard/events/store) crea un registro Location, luego consulta Nominatim con la dirección combinada y guarda las coordenadas devueltas si las encuentra — el evento lleva una ubicación real y mapeable, no solo una dirección de texto. Las actualizaciones solo re-geocodifican cuando la dirección o ciudad realmente cambian, así que editar un precio no vuelve a golpear la API de geocodificación.
Compra de Tickets con Seguimiento Automático de Capacidad
Comprar un ticket (POST /dashboard/tickets/store) está protegido por una sola verificación — rechazar si capacity < quantity o capacity == 0 — luego decrementa capacity, incrementa attendees, y cambia el estado del evento a Agotado en el momento en que la capacidad llega a cero, sin un paso administrativo aparte.
Recibos en PDF Bajo Demanda
Tanto un ticket (GET /dashboard/tickets/{id}/report) como un reporte completo del evento (GET /events/{id}/report, de acceso público) se renderizan vía DomPDF desde una plantilla Blade — trayendo la relación del evento precargada, formateando fechas con Carbon, y calculando el total (quantity × price) en el momento del render en vez de guardar un total desactualizado.
Roles de Dos Niveles Sin un Framework de Permisos
El control de acceso es una comparación de strings en minúsculas en un middleware personalizado contra los roles declarados de una ruta (role:admin|user) — sin clases de política, sin bit-masks de permisos, sin paquete extra. Las secciones exclusivas de admin (gestión de usuarios, categorías, estados) son simplemente rutas que el rol user nunca coincide.
Destacados Técnicos
Las transiciones de estado son un efecto secundario, no un flujo aparte
Disponible/Agotado no es un campo que un admin fija manualmente en operación normal — se deriva automáticamente de la matemática de capacidad en cada compra de ticket, y solo Finalizado (evento concluido) es una acción explícita del admin. Un estado menos que la UI tiene que gestionar a mano.
La re-geocodificación inteligente evita llamadas API desperdiciadas
El camino de actualización solo vuelve a consultar Nominatim cuando los campos de dirección o ciudad realmente cambiaron — cualquier otra edición (precio, capacidad, descripción) se salta por completo el viaje de ida y vuelta de geocodificación.
Una brecha de concurrencia conocida, nombrada en vez de escondida
La verificación de capacidad (capacity < quantity) no tiene bloqueo a nivel de base de datos, así que existe una condición de carrera genuina bajo compras concurrentes de los últimos tickets — vale la pena señalarlo explícitamente en vez de presentar el flujo de ticketing como infalible; un despliegue de producción lo envolvería en lockForUpdate().
Estructura del Proyecto
app/
├── Http/Controllers/
│ ├── EventController.php # Crear/actualizar/eliminar + geocodificación + subida de imagen
│ ├── TicketController.php # Flujo de compra + actualización de capacidad/estado + reporte PDF
│ ├── CategoryController.php # CRUD de admin
│ └── ...
├── Models/
│ ├── Event.php # belongsTo User/Location/Category/Status, hasMany Ticket
│ ├── Ticket.php # belongsTo User, Event
│ ├── Location.php # dirección + lat/lon geocodificados
│ ├── Category.php / Status.php
│ └── User.php / Role.php
├── Http/Middleware/
│ └── RoleMiddleware.php # guardia de ruta role:admin|user
└── Http/Resources/ # transformadores de respuesta de la API
database/
├── migrations/ # 6 tablas relacionadas, todas con soft-delete
└── seeders/
└── CategoryTableSeeder.php # Musical, Teatro, Deportivo, Cultural, Familiar, Gastronómico
resources/views/
└── reports/
└── ticket-report.blade.php # Plantilla DomPDF para reportes de tickets y eventosImpacto y Escalabilidad
- Una plataforma, ciclo de vida completo: listado, sede geocodificada, venta de tickets y recibos, sin combinar servicios separados
- Superficie de API pública: CRUD completo para eventos/categorías, solo lectura para ubicaciones/estados, así la plataforma puede integrarse más allá de su propio dashboard
- Autorización simple y auditable: un sistema de dos roles fácil de razonar, al costo de no soportar permisos más granulares si la plataforma crece
- Honesta sobre sus límites: la brecha de concurrencia documentada en la compra de tickets es una consideración real y nombrada para escalar a ventas de alta concurrencia
Notas
Construido con Laravel 10, MySQL, React y Bootstrap 5. El código es público en GitHub. Para un análisis técnico más profundo, consulta la wiki de documentación completa.