Reestructuración con módulos de Python y SOLID

Clase 13 de 27Curso de Patrones de Diseño y SOLID en Python

Resumen

Aplicar SOLID con módulos de Python permite pasar de un archivo monolítico a un diseño limpio, escalable y mantenible. Aquí verás cómo identificar contextos, separar responsabilidades y usar protocolos para desacoplar el alto nivel del bajo nivel en un Payment Service sin cambiar su lógica principal.

¿Cómo reestructurar un payment service con módulos de Python?

La técnica central es identificar contextos y responsabilidades de las clases de bajo nivel, y convertir cada contexto en un módulo. El Payment Service opera como clase de alto nivel, mientras validadores, logger, notificador y procesadores son bajo nivel y viven en módulos separados.

  • Parte de SOLID: inversión de dependencias y responsabilidad única.
  • Define contextos: validaciones, logging, notificaciones, procesadores de pago.
  • Crea carpetas-módulo con archivos por clase y un init.py como puerta de acceso.
  • Importa en el servicio solo protocolos de lo que varía (procesadores, notificador) y clases concretas de lo estable (validadores, logger).
  • Beneficios: mantenibilidad, escalabilidad y cambio seguro por archivo y contexto.

¿Qué contextos definen los módulos y clases?

Esta organización agrupa clases afines y estandariza sus puntos de entrada con init.py para facilitar importaciones, incluso con import all cuando haga sentido.

  • Commons.
  • ContactInfo: datos de contacto del customer.
  • CustomerData: datos del cliente.
  • PaymentData: incluye Currency agregado para soportar nuevos procesadores.
  • PaymentsResponse: respuesta del pago.
  • init.py: reexporta clases para importación centralizada.
  • Notifiers.
  • NotifierProtocol: protocolo con método send confirmation.
  • Email y SMS: implementaciones del protocolo.
  • Nota práctica: en Python no es obligatorio que la clase herede explícitamente del protocolo si cumple su interfaz; el editor puede advertir inconsistencias.
  • Loggers.
  • Transaction Logger: clase de logging de transacciones sin protocolo.
  • Validators.
  • Customer Validator y Payment Data Validator: validaciones sin protocolo, misma lógica previa.
  • Processors.
  • Protocolos: payment, recurring y reembolsos.
  • Implementaciones: Stripe (lógica principal de cobro, reembolso y recurrencia), offline processor (ejemplo para efectivo) y procesador local (ejemplo para futuras referencias).

¿Por qué usar protocolos en Python?

  • Permiten programar contra abstracciones sin acoplarse a implementaciones.
  • Cambias de Stripe a otro procesador sin tocar el Payment Service.
  • El editor ayuda a verificar que la clase cumple el contrato del protocolo.

¿Qué aporta init.py al módulo?

  • Unifica puntos de entrada de cada contexto.
  • Facilita importaciones claras desde la carpeta del módulo.
  • Permite definir listas públicas para import all cuando se requiera.

¿Cómo escalar con nuevos procesadores?

  • Agrega una clase nueva en Processors que implemente los tres protocolos o los que necesites.
  • Extiende datos en Commons (como Currency en PaymentData) cuando haga falta.
  • No toques el Payment Service: ya depende de protocolos.

¿Cómo se instancia e importa el servicio de alto nivel?

El archivo Service.py mantiene el mismo código del principio de inversión de dependencias; cambian solo las rutas de importación. La recomendación es crear main.py para instanciar el servicio y orquestar dependencias.

  • Importa el servicio de alto nivel desde service.
  • Importa de processors y notifiers solo los protocolos en el servicio; en main.py sí eliges implementaciones concretas.
  • Importa validadores y logger como clases concretas.
  • Instancia el Payment Service con las dependencias concretas y cámbialas cuando necesites.
# main.py
from service import PaymentService
from processors import Stripe  # implementación elegida
from notifiers import Email    # implementación elegida
from loggers import TransactionLogger
from validators import CustomerValidator, PaymentDataValidator

if __name__ == "__main__":
    payment_processor = Stripe()
    email_notifier = Email()
    customer_validator = CustomerValidator()
    payment_data_validator = PaymentDataValidator()
    logger = TransactionLogger()

    service = PaymentService(
        payment_processor=payment_processor,
        notifier=email_notifier,
        customer_validator=customer_validator,
        payment_data_validator=payment_data_validator,
        logger=logger,
    )

¿Te gustaría comentar cómo organizarías tus módulos o qué protocolo agregarías primero? Comparte tu enfoque y mejoras posibles para el Payment Service.