Refactorización Ticket Home: Código Limpio Y Eficiente

by Admin 55 views
♻️ Refactorización Completa: Arquitectura Limpia y Separación de Responsabilidades

¿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.py con 746 líneas (¡debería ser menos de 200!), commands.py con 720 líneas y routes/tickets.py con 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 tickets
  • FPACalculator - Extraer lógica de cálculo FPA de models.py
  • UserService - Gestión de usuarios y permisos
  • AuditService - Centralizar logging de acciones
  • PatientService - Lógica de pacientes
  • DashboardService - Lógica de métricas y estadísticas

4.2. Crear Capa de Repositories

  • TicketRepository - Queries complejas de tickets
  • UserRepository - Queries de usuarios
  • PatientRepository - Queries de pacientes
  • AuditRepository - Queries de auditoría

4.3. Crear Validadores

  • TicketValidator - Validar datos de tickets
  • UserValidator - Validar datos de usuarios
  • DateValidator - Validar fechas y FPA

4.4. Refactorizar Routes (Controllers)

  • routes/tickets.py - Reducir de 634 a ~150 líneas
  • routes/admin.py - Reducir de 746 a ~200 líneas
  • routes/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 fechas
  • string_utils.py - generate_prefix, formateo
  • decorators.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:

  1. Fase 1: Setup (2 días)

    • Crear estructura de carpetas.
    • Configurar imports y namespaces.
  2. Fase 2: Services (3 días)

    • Crear FPACalculator.
    • Crear TicketService.
    • Crear AuditService.
  3. Fase 3: Repositories (2 días)

    • Crear repositories básicos.
    • Migrar queries complejas.
  4. Fase 4: Refactorizar Routes (3 días)

  5. Fase 5: Testing (2 días)

    • Tests unitarios de services.
    • Tests de integración.
  6. 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

📝 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!