Refactorización Ticket Home: Código Limpio Y Eficiente
¿Estás listo para darle un lavado de cara a tu proyecto Ticket Home? Este artículo es tu guía definitiva para una refactorización completa, transformando el código en algo más limpio, mantenible y escalable. Vamos a sumergirnos en el mundo de la arquitectura limpia, la separación de responsabilidades y las mejores prácticas de desarrollo.
🎯 Motivación: ¿Por Qué Necesitamos Refactorizar?
La situación actual del código de Ticket Home no es la ideal. Tenemos problemas serios que dificultan el desarrollo y el mantenimiento. Algunos de los problemas más evidentes son:
- Archivos Monstruosos:
routes/admin.pycon 746 líneas (¡debería ser menos de 200!),commands.pycon 720 líneas yroutes/tickets.pycon 634 líneas. Demasiado código en un solo lugar, ¿verdad, amigos? - Mezcla Explosiva: Lógica de negocio mezclada con los controladores. Esto es un gran NO-NO. Los controladores deben orquestar, no hacer todo el trabajo.
- Código Clonado: Duplicación de código, como la función
calculate_time_remaining, que aparece dos veces. ¡No queremos repetirnos! ¡DRY (Don't Repeat Yourself) es la clave! - Falta de Estructura: Ausencia de una capa de servicios. Toda la lógica reside en las rutas, lo que es un caos.
- Modelos Sobrecargados: Modelos con demasiadas responsabilidades, lo que dificulta su comprensión y modificación.
- Validación Inexistente: Ausencia de validadores centralizados. Validar datos en cada ruta es tedioso y propenso a errores.
- Pruebas Difíciles: Dificultad para realizar pruebas unitarias efectivas. Un código bien estructurado es un código fácil de probar.
En resumen, necesitamos una refactorización para mejorar la calidad, la mantenibilidad y la escalabilidad de la aplicación. ¡Es hora de poner orden!
🏗️ Objetivos de Refactorización: El Camino a Seguir
Nuestros objetivos principales se centran en la separación de responsabilidades y la adopción de una arquitectura limpia.
1. Separación de Responsabilidades (Principios SOLID)
Vamos a aplicar los principios SOLID para crear una estructura de código robusta y fácil de mantener:
- Crear la Capa de Services: Aquí residirá la lógica de negocio. Cada servicio se encargará de una tarea específica, como la creación de tickets, el cálculo de tiempos o la gestión de usuarios.
- Crear la Capa de Repositories: Esta capa se encargará del acceso a datos, interactuando con la base de datos de manera eficiente. Los repositories proporcionarán métodos para obtener, guardar y eliminar datos.
- Crear la Capa de Validators: Los validadores se encargarán de la validación de datos, asegurando que la información que entra en el sistema sea correcta y segura. ¡Adiós a las validaciones inline!
- Crear la Capa de DTOs: Los Data Transfer Objects (DTOs) se utilizarán para transferir datos entre las diferentes capas de la aplicación, desacoplando la lógica de negocio de la estructura de datos.
- Mantener Routes como Controllers: Las rutas (controllers) solo se encargarán de orquestar las acciones, llamando a los servicios y devolviendo las respuestas. ¡Controladores delgados!
2. Estructura de Carpetas Propuesta: Un Nuevo Comienzo
Para lograr esta separación de responsabilidades, proponemos la siguiente estructura de carpetas. Imaginen esto como el plano de nuestra nueva casa:
ticket-home/
├── app/
│ ├── __init__.py # Factory de app Flask
│ ├── models/ # Modelos SQLAlchemy
│ │ ├── __init__.py
│ │ ├── user.py
│ │ ├── clinic.py
│ │ ├── patient.py
│ │ ├── ticket.py
│ │ └── audit.py
│ ├── services/ # Lógica de negocio (NUEVO)
│ │ ├── __init__.py
│ │ ├── ticket_service.py # Crear, modificar, anular tickets
│ │ ├── fpa_calculator.py # Cálculo FPA (extraído de models)
│ │ ├── user_service.py # Gestión de usuarios
│ │ └── audit_service.py # Logging de acciones
│ ├── repositories/ # Acceso a datos (NUEVO)
│ │ ├── __init__.py
│ │ ├── ticket_repository.py
│ │ ├── user_repository.py
│ │ └── patient_repository.py
│ ├── validators/ # Validación de datos (NUEVO)
│ │ ├── __init__.py
│ │ ├── ticket_validator.py
│ │ └── user_validator.py
│ ├── dto/ # Data Transfer Objects (NUEVO)
│ │ ├── __init__.py
│ │ ├── ticket_dto.py
│ │ └── user_dto.py
│ ├── routes/ # Controllers (REFACTORIZAR)
│ │ ├── __init__.py
│ │ ├── tickets.py # Reducir de 634 a ~150 líneas
│ │ ├── admin.py # Reducir de 746 a ~200 líneas
│ │ ├── dashboard.py
│ │ └── auth.py
│ ├── utils/ # Utilidades compartidas
│ │ ├── __init__.py
│ │ ├── datetime_utils.py # Funciones de fechas
│ │ ├── string_utils.py # generate_prefix, etc.
│ │ └── decorators.py # admin_required, etc.
│ └── config/ # Configuración
│ ├── __init__.py
│ ├── development.py
│ └── production.py
├── tests/ # Tests organizados por capa
│ ├── unit/
│ │ ├── test_services/
│ │ ├── test_repositories/
│ │ └── test_validators/
│ └── integration/
│ └── test_routes/
├── migrations/ # Migraciones Alembic
├── app.py # Entry point
└── requirements.txt
Esta estructura nos permite organizar el código de manera clara y concisa, facilitando la mantenibilidad y la escalabilidad.
3. Ejemplos de Refactorización: Del Caos a la Claridad
Veamos cómo era el código ANTES y cómo será DESPUÉS de la refactorización. La diferencia es abismal, ¡prepárense!
❌ ANTES (routes/tickets.py - 634 líneas)
El código actual en routes/tickets.py es un desastre. Lógica de validación, lógica de negocio y persistencia mezcladas en un solo lugar. ¡Un verdadero espagueti!
@tickets_bp.route('/create', methods=['POST'])
def create():
# Validación inline (20 líneas)
if not request.form.get('rut'):
flash('RUT es requerido', 'error')
return redirect(...)
# Lógica de negocio inline (50 líneas)
patient = Patient.query.filter_by(rut=rut).first()
if not patient:
patient = Patient(...)
db.session.add(patient)
# Cálculo FPA inline (30 líneas)
base_hours = surgery.base_stay_hours
fpa = pavilion_end_time + timedelta(hours=base_hours)
# ... más lógica compleja ...
# Persistencia inline (15 líneas)
db.session.commit()
flash('Ticket creado', 'success')
✅ DESPUÉS (routes/tickets.py - ~150 líneas)
Con la refactorización, el código se vuelve limpio y fácil de entender. La responsabilidad de cada parte del proceso está claramente definida.
@tickets_bp.route('/create', methods=['POST'])
def create():
# 1. Validar entrada
validation_errors = TicketValidator.validate_create(request.form)
if validation_errors:
return jsonify({'errors': validation_errors}), 400
# 2. Convertir a DTO
ticket_data = TicketDTO.from_form(request.form)
# 3. Ejecutar lógica de negocio (service)
try:
ticket = TicketService.create_ticket(ticket_data, current_user)
flash('Ticket creado exitosamente', 'success')
return redirect(url_for('tickets.nursing_board'))
except BusinessException as e:
flash(str(e), 'error')
return redirect(url_for('tickets.create'))
🧩 Service Layer (NUEVO)
La capa de servicios es el corazón de nuestra aplicación refactorizada. Aquí es donde reside la lógica de negocio. Este es un ejemplo de cómo se verá TicketService:
# app/services/ticket_service.py
class TicketService:
@staticmethod
def create_ticket(ticket_data: TicketDTO, user: User) -> Ticket:
"""Crea un ticket con toda la lógica de negocio centralizada."""
# 1. Obtener/crear paciente
patient = PatientRepository.get_or_create(
rut=ticket_data.patient_rut,
clinic_id=user.clinic_id
)
# 2. Calcular FPA
fpa_info = FPACalculator.calculate(
pavilion_end_time=ticket_data.pavilion_end_time,
surgery=ticket_data.surgery
)
# 3. Generar ID
ticket_id = TicketIDGenerator.generate(user.clinic_id)
# 4. Crear ticket
ticket = Ticket(
id=ticket_id,
patient=patient,
system_calculated_fpa=fpa_info.fpa,
# ... más campos ...
)
# 5. Auditar acción
AuditService.log_action(
user=user,
action='create_ticket',
target=ticket
)
# 6. Persistir
TicketRepository.save(ticket)
return ticket
¡La diferencia es notable! Código limpio, organizado y fácil de entender. ¡Es mucho más fácil de mantener y probar!
4. Tareas Específicas: El Plan de Ataque
Aquí están las tareas específicas que debemos realizar para lograr la refactorización:
4.1. Crear Capa de Servicios
TicketService- Crear, modificar, anular ticketsFPACalculator- Extraer lógica de cálculo FPA demodels.pyUserService- Gestión de usuarios y permisosAuditService- Centralizar logging de accionesPatientService- Lógica de pacientesDashboardService- Lógica de métricas y estadísticas
4.2. Crear Capa de Repositories
TicketRepository- Queries complejas de ticketsUserRepository- Queries de usuariosPatientRepository- Queries de pacientesAuditRepository- Queries de auditoría
4.3. Crear Validadores
TicketValidator- Validar datos de ticketsUserValidator- Validar datos de usuariosDateValidator- Validar fechas y FPA
4.4. Refactorizar Routes (Controllers)
routes/tickets.py- Reducir de 634 a ~150 líneasroutes/admin.py- Reducir de 746 a ~200 líneasroutes/dashboard.py- Simplificar queries
4.5. Refactorizar Models
- Separar modelos en archivos individuales
- Mover lógica de negocio a Services
- Mantener solo propiedades y relaciones
4.6. Eliminar Código Duplicado
- Unificar
calculate_time_remaining(aparece en tickets.py y utils.py) - Centralizar decoradores (
admin_required,superuser_required) - Extraer funciones comunes de
commands.py
4.7. Mejorar Utilidades
datetime_utils.py- Funciones de fechasstring_utils.py-generate_prefix, formateodecorators.py- Centralizar decoradores
5. Principios a Aplicar: La Filosofía Detrás del Código
Durante la refactorización, nos guiaremos por los siguientes principios:
SOLID
- Single Responsibility: Cada clase/función hace una sola cosa.
- Open/Closed: Abierto a extensión, cerrado a modificación.
- Liskov Substitution: Interfaces consistentes.
- Interface Segregation: Interfaces pequeñas y específicas.
- Dependency Inversion: Depender de abstracciones.
DRY (Don't Repeat Yourself)
- Eliminar código duplicado.
- Centralizar lógica común.
- Reutilizar componentes.
KISS (Keep It Simple, Stupid)
- Código claro y simple.
- Evitar complejidad innecesaria.
- Funciones pequeñas (<50 líneas).
6. Métricas de Éxito: ¿Cómo Mediremos el Progreso?
Para asegurarnos de que la refactorización es un éxito, utilizaremos las siguientes métricas:
Antes de Refactorización:
routes/admin.py: 746 líneas
routes/tickets.py: 634 líneas
commands.py: 720 líneas
Total: 2100 líneas en 3 archivos
Después de Refactorización:
routes/admin.py: ~200 líneas (controllers puros)
routes/tickets.py: ~150 líneas (controllers puros)
commands.py: ~200 líneas (comandos CLI)
services/: ~600 líneas (lógica de negocio)
repositories/: ~300 líneas (queries)
validators/: ~200 líneas (validación)
Total: 1650 líneas bien organizadas
Beneficios Medibles:
- ✅ Reducción de ~21% en total de líneas.
- ✅ Archivos individuales <250 líneas (estándar: <200).
- ✅ Separación clara de responsabilidades.
- ✅ Testing más fácil (unit tests de services).
- ✅ Menos código duplicado.
- ✅ Mayor cohesión, menor acoplamiento.
7. Plan de Ejecución: El Camino Paso a Paso
Aquí está el plan de trabajo detallado para la refactorización, como un buen mapa del tesoro:
-
Fase 1: Setup (2 días)
- Crear estructura de carpetas.
- Configurar imports y namespaces.
-
Fase 2: Services (3 días)
- Crear FPACalculator.
- Crear TicketService.
- Crear AuditService.
-
Fase 3: Repositories (2 días)
- Crear repositories básicos.
- Migrar queries complejas.
-
Fase 4: Refactorizar Routes (3 días)
- Refactorizar tickets.py.
- Refactorizar admin.py.
-
Fase 5: Testing (2 días)
- Tests unitarios de services.
- Tests de integración.
-
Fase 6: Limpieza (1 día)
- Eliminar código muerto.
- Actualizar documentación.
Total: ~13 días de desarrollo
✅ Definition of Done: ¿Cómo Sabremos que Hemos Terminado?
Para asegurar la calidad, definimos los siguientes criterios de "Hecho":
- ✅ Todos los archivos <250 líneas.
- ✅ Capa de services implementada.
- ✅ Capa de repositories implementada.
- ✅ Sin código duplicado.
- ✅ Todos los tests pasan.
- ✅ Coverage >80%.
- ✅ Documentación actualizada.
- ✅ Code review aprobado.
- ✅ Sin regresiones funcionales.
🔗 Referencias: Para Aprender Más
- Clean Architecture - Robert C. Martin - ¡La biblia de la arquitectura limpia!
- SOLID Principles - Profundiza en los principios SOLID.
- Flask Best Practices - Consejos de Flask para el éxito.
- Repository Pattern - Aprende sobre el patrón Repository.
📝 Notas: ¡A Refactorizar!
Esta refactorización NO cambiará la funcionalidad existente. Solo reorganizará el código para que sea más fácil de mantener, escalar y probar. ¡A darle con todo!