Events Service
1. Descripción
Microservicio responsable de la gestión del ciclo de vida completo de eventos, desde su creación en estado borrador hasta su finalización. Implementa la lógica de negocio del agregado Evento según el lenguaje ubicuo de la plataforma.
Bounded Context: Gestión de Eventos
Repository: eventmesh-lab/events-service
2. Responsabilidades
- Crear y editar eventos en estado borrador
- Coordinar el proceso de pago de publicación
- Publicar eventos en el catálogo tras confirmación de pago
- Gestionar el estado del evento durante su ciclo de vida
- Administrar secciones y precios por tipo de entrada
- Emitir eventos de dominio para comunicación asíncrona
3. Modelo de Dominio
3.1 Agregado: Evento
Root Aggregate: Evento
Entidades
Evento
public class Evento : AggregateRoot
{
public Guid Id { get; private set; }
public string Nombre { get; private set; }
public string Descripcion { get; private set; }
public FechaEvento FechaInicio { get; private set; }
public DuracionEvento Duracion { get; private set; }
public EstadoEvento Estado { get; private set; }
public Guid OrganizadorId { get; private set; }
public Guid VenueId { get; private set; }
public string Categoria { get; private set; }
public IReadOnlyList<Seccion> Secciones { get; private set; }
public DateTime FechaCreacion { get; private set; }
public DateTime? FechaPublicacion { get; private set; }
}
Seccion
public class Seccion : Entity
{
public Guid Id { get; private set; }
public string Nombre { get; private set; }
public PrecioEntrada PrecioBase { get; private set; }
public int Capacidad { get; private set; }
public string TipoAsiento { get; private set; } // Numerado, General
}
Value Objects
FechaEvento
public record FechaEvento
{
public DateTime Fecha { get; init; }
public TimeZoneInfo ZonaHoraria { get; init; }
// Validaciones: Fecha no puede ser en el pasado
}
DuracionEvento
public record DuracionEvento
{
public TimeSpan Duracion { get; init; }
// Validaciones: Duración debe ser positiva y menor a 24 horas
}
EstadoEvento
public enum EstadoEvento
{
Borrador, // Evento creado, aún no pagado
PendientePago, // Pago iniciado, esperando confirmación
Publicado, // Visible en el catálogo
EnCurso, // Evento en ejecución
Finalizado, // Evento terminado
Cancelado // Evento cancelado por el organizador
}
PrecioEntrada
public record PrecioEntrada
{
public decimal Monto { get; init; }
public string Moneda { get; init; } // USD, EUR, etc.
// Validaciones: Monto debe ser positivo
}
4. Comandos del Dominio
CrearEvento
Descripción: El organizador registra un nuevo evento en estado Borrador.
Input:
public record CrearEventoCommand
{
public string Nombre { get; init; }
public string Descripcion { get; init; }
public DateTime FechaInicio { get; init; }
public TimeSpan Duracion { get; init; }
public Guid OrganizadorId { get; init; }
public Guid VenueId { get; init; }
public string Categoria { get; init; }
public List<SeccionDto> Secciones { get; init; }
}
Validaciones: - El organizador debe existir y estar activo - El venue debe existir y estar disponible - La fecha no puede ser en el pasado - Al menos una sección debe estar definida
Emite: EventoCreado
EditarEvento
Descripción: El organizador modifica datos del evento antes de la publicación.
Input:
public record EditarEventoCommand
{
public Guid EventoId { get; init; }
public string Nombre { get; init; }
public string Descripcion { get; init; }
public DateTime FechaInicio { get; init; }
public List<SeccionDto> Secciones { get; init; }
}
Validaciones:
- El evento debe estar en estado Borrador
- El organizador debe ser el propietario del evento
Emite: EventoEditado
PagarPublicacion
Descripción: El organizador inicia el pago que habilita la publicación del evento.
Input:
public record PagarPublicacionCommand
{
public Guid EventoId { get; init; }
public Guid TransaccionPagoId { get; init; }
}
Validaciones:
- El evento debe estar en estado Borrador
- El monto del pago debe coincidir con la tarifa de publicación
Emite: PagoPublicacionIniciado
Estado resultante: PendientePago
PublicarEvento
Descripción: El sistema marca el evento como publicado tras confirmar el pago.
Input:
public record PublicarEventoCommand
{
public Guid EventoId { get; init; }
public Guid PagoConfirmadoId { get; init; }
}
Validaciones:
- El evento debe estar en estado PendientePago
- El pago debe estar confirmado
Emite: EventoPublicado
Estado resultante: Publicado
IniciarEvento
Descripción: El sistema marca el evento como en curso cuando llega la fecha de inicio.
Input:
public record IniciarEventoCommand
{
public Guid EventoId { get; init; }
}
Validaciones:
- El evento debe estar en estado Publicado
- La fecha actual debe ser posterior o igual a la fecha de inicio
Emite: EventoIniciado
Estado resultante: EnCurso
FinalizarEvento
Descripción: El sistema marca el evento como finalizado.
Input:
public record FinalizarEventoCommand
{
public Guid EventoId { get; init; }
}
Validaciones:
- El evento debe estar en estado EnCurso
Emite: EventoFinalizado
Estado resultante: Finalizado
5. Eventos de Dominio
EventoCreado
public record EventoCreado : DomainEvent
{
public Guid EventoId { get; init; }
public string Nombre { get; init; }
public Guid OrganizadorId { get; init; }
public DateTime FechaInicio { get; init; }
public DateTime FechaCreacion { get; init; }
}
Suscriptores:
- notifications-service: Envía confirmación al organizador
PagoPublicacionIniciado
public record PagoPublicacionIniciado : DomainEvent
{
public Guid EventoId { get; init; }
public Guid TransaccionPagoId { get; init; }
public decimal Monto { get; init; }
}
Suscriptores:
- payments-service: Procesa el pago
EventoPublicado
public record EventoPublicado : DomainEvent
{
public Guid EventoId { get; init; }
public string Nombre { get; init; }
public Guid OrganizadorId { get; init; }
public DateTime FechaPublicacion { get; init; }
}
Suscriptores:
- notifications-service: Notifica al organizador
- analytics-service: Registra métrica
- forums-service: Crea foro del evento
EventoEditado
public record EventoEditado : DomainEvent
{
public Guid EventoId { get; init; }
public DateTime FechaEdicion { get; init; }
}
EventoIniciado
public record EventoIniciado : DomainEvent
{
public Guid EventoId { get; init; }
public DateTime FechaInicio { get; init; }
}
Suscriptores:
- streaming-service: Habilita transmisión (si aplica)
- notifications-service: Recordatorio a asistentes
EventoFinalizado
public record EventoFinalizado : DomainEvent
{
public Guid EventoId { get; init; }
public DateTime FechaFinalizacion { get; init; }
}
Suscriptores:
- analytics-service: Genera reporte de asistencia
- streaming-service: Cierra transmisión
6. Reglas de Negocio
-
Publicación requiere pago confirmado: Un evento solo puede pasar de
BorradoraPublicadosi existe un pago confirmado de publicación. -
Edición limitada: Solo se pueden editar eventos en estado
Borrador. Una vez publicados, ciertos campos quedan bloqueados. -
Capacidad por sección: La suma de capacidades de todas las secciones define el aforo máximo del evento.
-
Fecha de inicio futura: Al crear o editar, la fecha de inicio debe ser posterior a la fecha actual.
-
Organizador autorizado: Solo el organizador propietario puede editar o cancelar el evento.
7. Integraciones
Servicios de Dominio
ServicioDePublicacion
public interface IServicioDePublicacion
{
Task<Result> ValidarYPublicarEvento(Guid eventoId, Guid pagoId);
}
Coordina la validación del pago y la transición de estado del evento.
Comunicación Asíncrona (RabbitMQ)
Exchange: eventos.domain.events
Publica:
- EventoCreado
- EventoPublicado
- EventoEditado
- EventoIniciado
- EventoFinalizado
Consume:
- PagoPublicacionConfirmado (desde payments-service)
8. Persistencia
Base de Datos: PostgreSQL
Tabla: eventos
CREATE TABLE eventos (
id UUID PRIMARY KEY,
nombre VARCHAR(200) NOT NULL,
descripcion TEXT,
fecha_inicio TIMESTAMP NOT NULL,
duracion INTERVAL NOT NULL,
estado VARCHAR(20) NOT NULL,
organizador_id UUID NOT NULL,
venue_id UUID NOT NULL,
categoria VARCHAR(100),
fecha_creacion TIMESTAMP NOT NULL,
fecha_publicacion TIMESTAMP,
version INT NOT NULL
);
Tabla: secciones
CREATE TABLE secciones (
id UUID PRIMARY KEY,
evento_id UUID NOT NULL REFERENCES eventos(id),
nombre VARCHAR(100) NOT NULL,
precio_monto DECIMAL(10,2) NOT NULL,
precio_moneda VARCHAR(3) NOT NULL,
capacidad INT NOT NULL,
tipo_asiento VARCHAR(20) NOT NULL
);
9. API Endpoints
POST /api/eventos
Crea un nuevo evento en estado borrador.
Request:
{
"nombre": "Concierto Rock 2025",
"descripcion": "Gran concierto de rock",
"fechaInicio": "2025-12-15T20:00:00Z",
"duracion": "03:00:00",
"organizadorId": "uuid",
"venueId": "uuid",
"categoria": "Música",
"secciones": [
{
"nombre": "VIP",
"precioMonto": 150.00,
"precioMoneda": "USD",
"capacidad": 100,
"tipoAsiento": "Numerado"
}
]
}
Response: 201 Created
PUT /api/eventos/{id}
Edita un evento existente en estado borrador.
POST /api/eventos/{id}/pagar-publicacion
Inicia el proceso de pago de publicación.
GET /api/eventos/{id}
Obtiene los detalles de un evento.
GET /api/eventos
Lista eventos publicados con filtros y paginación.
Query Params:
- categoria: Filtra por categoría
- fechaDesde: Filtra por fecha mínima
- organizadorId: Filtra por organizador
- page: Número de página
- pageSize: Tamaño de página
10. Tecnologías
- .NET 8 (Minimal APIs)
- Entity Framework Core 8 (PostgreSQL)
- MediatR (CQRS pattern)
- FluentValidation (Validaciones)
- RabbitMQ.Client (Mensajería)
- Serilog (Logging)
- OpenTelemetry (Observabilidad)
11. Observabilidad
Métricas
eventos_creados_total: Contador de eventos creadoseventos_publicados_total: Contador de eventos publicadostiempo_publicacion_segundos: Histograma de tiempo entre creación y publicación
Logs Estructurados
EventoCreado: Nivel INFOEventoPublicado: Nivel INFOErrorValidacion: Nivel WARNINGErrorIntegracion: Nivel ERROR
Traces
- Span principal:
POST /api/eventos - Spans secundarios: Validaciones, persistencia, publicación de eventos
12. Testing
Pruebas Unitarias
- Comandos y validaciones
- Lógica de agregados
- Value objects
Pruebas de Integración
- Endpoints API
- Persistencia en PostgreSQL
- Publicación de eventos en RabbitMQ
Pruebas de Contratos
- Schema de eventos de dominio