Saltar a contenido

Modelo de Dominio: Scheduling (Reservas)


Proposito

Modelo de dominio completo del bounded context de Scheduling. Este es el contexto core del sistema: gestiona la disponibilidad de medicos y la reserva de turnos.


Metadata

Campo Valor
Bounded Context Scheduling
Domain Expert Laura Martinez (Product Owner)
Autor tecnico Carlos Ramirez
Fecha 2025-03-25
Estado Validated

1. Descripcion del Contexto

El contexto de Scheduling es responsable de gestionar la disponibilidad de medicos y permitir a pacientes reservar, cancelar, y consultar turnos. Es el corazon del sistema: sin Scheduling no hay producto.

NO es responsabilidad de Scheduling: la autenticacion (Identity), la gestion de clinicas (Clinic Management), ni el envio de notificaciones (Notification). Scheduling emite eventos que otros contextos consumen.


2. Entidades (Entities)

Booking (Turno)

Identidad: UUID autogenerado al momento de la reserva.

Atributos clave: | Atributo | Tipo | Descripcion | |----------|------|-------------| | id | UUID | Identificador unico del turno | | patientId | UUID | Referencia al paciente (del contexto Identity) | | doctorId | UUID | Referencia al medico | | clinicId | UUID | Referencia a la clinica | | specialtyId | UUID | Especialidad de la consulta | | date | Date | Fecha del turno | | timeRange | TimeRange (VO) | Hora inicio y fin | | status | BookingStatus (VO) | Estado actual del turno | | cancellationPenalty | boolean | Si la cancelacion genero penalizacion |

Invariantes: - Un booking siempre tiene paciente, medico, clinica, y especialidad. - La fecha del booking debe ser futura al momento de la creacion. - El timeRange debe estar dentro de la disponibilidad del medico. - Solo se puede cancelar un booking con status CONFIRMED. - Un booking completado o no-show no puede cambiar de estado.

Ciclo de vida:

CONFIRMED -> CANCELLED (por paciente o medico)
CONFIRMED -> COMPLETED (medico marca atencion realizada)
CONFIRMED -> NO_SHOW (medico marca que el paciente no vino)

DoctorAvailability (Disponibilidad del Medico)

Identidad: UUID.

Atributos clave: | Atributo | Tipo | Descripcion | |----------|------|-------------| | id | UUID | Identificador unico | | doctorId | UUID | Medico al que pertenece | | clinicId | UUID | Clinica donde atiende en este horario | | dayOfWeek | DayOfWeek (VO) | Dia de la semana (0-6) | | timeRange | TimeRange (VO) | Hora inicio y fin de atencion | | slotDurationMinutes | integer | Duracion de cada slot |

Invariantes: - Un medico no puede tener dos disponibilidades que se solapen en el mismo dia y clinica. - Un medico no puede tener disponibilidades en dos clinicas diferentes que se solapen en horario. - La duracion del slot debe ser 15, 20, 30, 45, o 60 minutos. - El timeRange debe ser de al menos un slot de duracion.

BlockedDate (Dia Bloqueado)

Identidad: UUID.

Atributos clave: | Atributo | Tipo | Descripcion | |----------|------|-------------| | id | UUID | Identificador unico | | doctorId | UUID | Medico | | date | Date | Dia bloqueado | | reason | string (opcional) | Motivo del bloqueo |

Invariantes: - La fecha debe ser futura. - No puede haber dos blocked dates para el mismo medico y fecha.


3. Value Objects

TimeRange (Rango Horario)

Atributos: | Atributo | Tipo | Validacion | |----------|------|------------| | startTime | Time | HH:mm, debe ser < endTime | | endTime | Time | HH:mm, debe ser > startTime |

Reglas de creacion: - startTime debe ser anterior a endTime. - La diferencia minima es 15 minutos. - Se expresan en la zona horaria de la clinica.

BookingStatus

Valores posibles: CONFIRMED, CANCELLED, COMPLETED, NO_SHOW

Transiciones validas: - CONFIRMED -> CANCELLED | COMPLETED | NO_SHOW - Los demas son estados finales.

DayOfWeek

Valores: 0 (Domingo) a 6 (Sabado)

TimeSlot (Slot Disponible - Calculado)

Atributos: | Atributo | Tipo | Validacion | |----------|------|------------| | date | Date | Fecha del slot | | timeRange | TimeRange | Horario del slot | | doctorId | UUID | Medico | | clinicId | UUID | Clinica | | isAvailable | boolean | Si esta libre o no |

Nota: TimeSlot NO se persiste en la BD. Se calcula on-the-fly a partir de DoctorAvailability - BlockedDates - Bookings existentes.


4. Aggregates

Booking Aggregate

Aggregate Root: Booking

Componentes: - Booking (entidad raiz) - TimeRange (value object) - BookingStatus (value object)

Invariantes del aggregate: - Un booking solo puede crearse si el slot esta disponible. - La cancelacion con < 24h genera penalizacion automaticamente. - Solo el paciente dueno o el medico asignado pueden cancelar.

Operaciones: - create(patientId, doctorId, clinicId, specialtyId, date, timeRange) -> BookingConfirmed event - cancel(cancelledBy, reason) -> BookingCancelled event - complete() -> BookingCompleted event - markNoShow() -> PatientNoShow event

DoctorSchedule Aggregate

Aggregate Root: Doctor (referencia, la entidad vive en Clinic Management)

Componentes: - DoctorAvailability[] (entidades) - BlockedDate[] (entidades) - TimeRange (value object) - DayOfWeek (value object)

Invariantes del aggregate: - Las disponibilidades no pueden solaparse. - Bloquear un dia no cancela bookings existentes (se notifica al paciente).

Operaciones: - addAvailability(clinicId, dayOfWeek, timeRange, slotDuration) - removeAvailability(availabilityId) - blockDate(date, reason) -> DateBlocked event - unblockDate(date)


5. Domain Events

Evento Trigger Datos Consumidores
BookingConfirmed Paciente reserva un turno bookingId, patientId, doctorId, clinicId, date, timeRange Notification (email confirmacion)
BookingCancelled Paciente o medico cancela bookingId, cancelledBy, reason, hasPenalty Notification (email cancelacion)
BookingCompleted Medico marca turno como completado bookingId, doctorId, patientId Analytics (futuro)
PatientNoShow Medico marca no-show bookingId, patientId Notification (email), Penalization logic
DateBlocked Medico bloquea un dia doctorId, date, affectedBookings[] Notification (avisar pacientes afectados)

6. Domain Services

AvailabilityCalculator

Responsabilidad: Calcular los slots disponibles de un medico en un rango de fechas.

Inputs: - doctorId, dateFrom, dateTo

Output: - Lista de TimeSlot con isAvailable = true

Reglas de negocio que implementa: - Genera slots a partir de DoctorAvailability (horario recurrente). - Resta los BlockedDates (dias completos bloqueados). - Resta los Bookings existentes con status CONFIRMED. - Filtra slots pasados (no muestra slots cuya hora ya paso). - Aplica la ventana de reserva (minimo 2h antes, maximo 30 dias adelante).

BookingConflictValidator

Responsabilidad: Verificar que un slot no este ocupado antes de crear un booking.

Inputs: - doctorId, date, timeRange

Output: - boolean (true si hay conflicto)

Reglas de negocio que implementa: - Verifica que no exista un booking CONFIRMED para el mismo medico, fecha, y timeRange. - Usa bloqueo pesimista (SELECT FOR UPDATE) para evitar race conditions.

PenalizationService

Responsabilidad: Gestionar las penalizaciones por cancelacion tardia.

Inputs: - patientId, bookingDate, cancellationTime

Output: - boolean (si aplica penalizacion), penaltyCount (total de penalizaciones activas)

Reglas de negocio que implementa: - Penalizacion si cancela con < 24h de anticipacion. - Bloqueo de 30 dias si acumula 3 penalizaciones. - Las penalizaciones se resetean despues de 6 meses sin nuevas.


7. Relaciones con Otros Contextos

Otro Contexto Tipo de Relacion Que se comparte
Identity & Access Customer/Supplier Scheduling consume User ID del JWT. No sabe nada del password o session.
Clinic Management Customer/Supplier Scheduling consume Doctor ID, Clinic ID, Specialty ID. Puede consultar nombre del medico para mostrar en la UI.
Notification Publisher/Subscriber Scheduling publica domain events. Notification los consume y envia emails. Scheduling no sabe ni le importa como se envia el email.

Checklist de Completitud

  • Entidades identificadas con invariantes
  • Value objects definidos con reglas de validacion
  • Aggregates delimitados (un aggregate por transaccion)
  • Domain events listados con consumidores
  • Domain services con reglas de negocio
  • Validado con domain expert (Laura Martinez)
  • Revisada por otro ingeniero

Archivos relacionados