The Medical Appointment Management API uses a service-oriented architecture where the service layer encapsulates all business logic and data operations. This design keeps controllers thin and focused solely on HTTP concerns.
public interface ICitaService{ List<Cita> ObtenerCitas(); Cita AgregarCita(CreateCitaDto cita); Cita ObtenerCita(int id); void EliminarCita(int id); Cita ActualizarCita(int id, CreateCitaDto cita);}
using System;using preliminarServicios.Models.Dtos;using preliminarServicios.Models.Entities;namespace preliminarServicios.Services;public class PacienteService : IPacienteService{ private readonly List<Paciente> _pacientes = []; private int _nextId = 1; public List<Paciente> ObtenerPacientes() { return _pacientes; } public Paciente AgregarPaciente(CreatePacienteDto pacienteDto) { var paciente = new Paciente { Id = _nextId++, Nombre = pacienteDto.Nombre, Apellido = pacienteDto.Apellido, Dni = pacienteDto.Dni, Email = pacienteDto.Email, Telefono = pacienteDto.Telefono, FechaNacimiento = pacienteDto.FechaNacimiento }; _pacientes.Add(paciente); return paciente; } public Paciente ObtenerPaciente(int id) { var paciente = _pacientes.FirstOrDefault(p => p.Id == id); if (paciente == null) throw new KeyNotFoundException($"Paciente con ID {id} no encontrado"); return paciente; } public void EliminarPaciente(int id) { var paciente = ObtenerPaciente(id); _pacientes.Remove(paciente); } public Paciente ActualizarPaciente(int id, CreatePacienteDto pacienteDto) { var paciente = ObtenerPaciente(id); paciente.Nombre = pacienteDto.Nombre; paciente.Apellido = pacienteDto.Apellido; paciente.Dni = pacienteDto.Dni; paciente.Email = pacienteDto.Email; paciente.Telefono = pacienteDto.Telefono; paciente.FechaNacimiento = pacienteDto.FechaNacimiento; return paciente; }}
The actual implementation in the source code currently throws NotImplementedException placeholders. The example above shows the intended implementation pattern.
Current Choice: The API uses singletons to maintain in-memory data across requests. This is suitable for development/prototyping but NOT for production.
public Paciente AgregarPaciente(CreatePacienteDto dto){ // Check for duplicate DNI if (_pacientes.Any(p => p.Dni == dto.Dni)) throw new InvalidOperationException($"Ya existe un paciente con DNI {dto.Dni}"); // Check for duplicate email if (_pacientes.Any(p => p.Email == dto.Email)) throw new InvalidOperationException($"Ya existe un paciente con email {dto.Email}"); // Validate age var age = DateOnly.FromDateTime(DateTime.Now).Year - dto.FechaNacimiento.Year; if (age < 0 || age > 150) throw new InvalidOperationException("Fecha de nacimiento inválida"); // Create entity var paciente = new Paciente { /* ... */ }; _pacientes.Add(paciente); return paciente;}
public Cita AgregarCita(CreateCitaDto dto){ // Validate dates if (dto.FechaInicio >= dto.FechaFin) throw new InvalidOperationException("La fecha de inicio debe ser anterior a la fecha de fin"); if (dto.FechaInicio < DateTime.Now) throw new InvalidOperationException("No se pueden crear citas en el pasado"); // Check doctor availability var conflictos = _citas.Where(c => c.MedicoId == dto.MedicoId && c.Estado != CitaEstado.Cancelada && ( (dto.FechaInicio >= c.FechaInicio && dto.FechaInicio < c.FechaFin) || (dto.FechaFin > c.FechaInicio && dto.FechaFin <= c.FechaFin) ) ); if (conflictos.Any()) throw new InvalidOperationException("El médico no está disponible en ese horario"); // Create appointment var cita = new Cita { /* ... */ }; _citas.Add(cita); return cita;}
public Paciente ObtenerPaciente(int id){ var paciente = _pacientes.FirstOrDefault(p => p.Id == id); if (paciente == null) throw new KeyNotFoundException($"Paciente con ID {id} no encontrado"); return paciente;}
Interface-based design makes services easy to test:
public class PacienteServiceTests{ [Fact] public void AgregarPaciente_CreatesPatientWithId() { // Arrange var service = new PacienteService(); var dto = new CreatePacienteDto { Nombre = "Test", Apellido = "User", Dni = "12345678A", Email = "[email protected]", FechaNacimiento = new DateOnly(1990, 1, 1) }; // Act var result = service.AgregarPaciente(dto); // Assert Assert.NotEqual(0, result.Id); Assert.Equal("Test", result.Nombre); }}
public class AppDbContext : DbContext{ public DbSet<Paciente> Pacientes { get; set; } public DbSet<Medico> Medicos { get; set; } public DbSet<Especialidad> Especialidades { get; set; } public DbSet<Cita> Citas { get; set; }}