Adapter

 

Patrón estructural adapter en golang

🚀 Patrón Estructural Adapter: Caso Real de Integración de Pagos

Hoy abordaremos un problema común en el desarrollo de software empresarial: la integración de sistemas legacy utilizando el patrón Adapter. ¡Prepárate para aprender haciendo! 💪

🌟 Escenario Real: Modernización del Sistema de Pagos de una Fintech

Imagina que trabajas para FinGoTech, una startup fintech en crecimiento. Tu equipo ha heredado un sistema de procesamiento de pagos antiguo, pero confiable, escrito en Go. El CTO quiere integrarlo con la nueva plataforma de microservicios que están construyendo. Aquí es donde brilla el patrón Adapter. 🌈

🧠 El Desafío

El sistema legacy utiliza una interfaz que no es compatible con los nuevos microservicios. Tu tarea: crear un adaptador que permita que el viejo sistema funcione sin problemas con la nueva arquitectura, sin modificar el código existente.

💻 Manos a la Obra: Implementando la Solución Paso a Paso

Vamos a desglosar cada paso de la implementación, explicando en detalle qué estamos haciendo y por qué. Este enfoque te ayudará a comprender profundamente cómo aplicar el patrón Adapter en situaciones del mundo real.

Paso 1: Analizar las Interfaces Existentes

Sé que ya quieres codear, pero recuerda, primero debemos entender qué necesitamos hacer. Por esto, nuestro primer paso será comprender las interfaces con las que estamos trabajando. Tenemos un sistema legacy y un nuevo sistema que se debe integrar.

// Sistema Legacy type LegacyPaymentProcessor struct{} func (lpp *LegacyPaymentProcessor) ProcessPayment(amount int, currency string) (string, error) { // Simulación de procesamiento de pago legacy return fmt.Sprintf("Payment of %d %s processed", amount, currency), nil } // Nueva Interfaz de Microservicios type ModernPaymentService interface { ExecuteTransaction(transaction Transaction) (TransactionResult, error) } type Transaction struct { Amount float64 Currency string } type TransactionResult struct { Success bool Message string }
  • El LegacyPaymentProcessor representa nuestro sistema antiguo. Nota que trabaja con amount como int, probablemente representando centavos.
  • ModernPaymentService es la nueva interfaz que nuestros microservicios esperan usar. Utiliza una estructura Transaction y devuelve un TransactionResult.
  • La diferencia clave está en cómo se representan los montos (int vs float64) y en la estructura de los datos de entrada y salida.

Paso 2: Diseñar e Implementar el Adapter

Ahora, crearemos nuestro Adapter para hacer que el sistema legacy sea compatible con la nueva interfaz.

// Nuestro Adapter type PaymentServiceAdapter struct { legacyProcessor *LegacyPaymentProcessor } func (psa *PaymentServiceAdapter) ExecuteTransaction(transaction Transaction) (TransactionResult, error) { // Convertir float64 a int (asumiendo que trabajamos con centavos) amountInCents := int(transaction.Amount * 100) // Llamar al método del sistema legacy result, err := psa.legacyProcessor.ProcessPayment(amountInCents, transaction.Currency) if err != nil { // Si hay un error, lo propagamos con un TransactionResult que indica fallo return TransactionResult{Success: false, Message: err.Error()}, err } // Si todo va bien, devolvemos un TransactionResult exitoso return TransactionResult{Success: true, Message: result}, nil }
  1. Definimos PaymentServiceAdapter que contiene una referencia al LegacyPaymentProcessor. Esto nos permite usar el sistema antiguo dentro del nuevo.
  2. Implementamos el método ExecuteTransaction que cumple con la interfaz ModernPaymentService. Este método es el corazón de nuestro Adapter.
  3. Dentro de ExecuteTransaction:
    • Convertimos el Amount de float64 a int multiplicando por 100. Esto es crucial porque el sistema legacy espera centavos como enteros.
    • Llamamos al método ProcessPayment del sistema legacy con los datos convertidos.
    • Manejamos el resultado:
      • Si hay un error, lo envolvemos en un TransactionResult con Success: false.
      • Si es exitoso, creamos un TransactionResult con Success: true y el mensaje del sistema legacy.
  4. Este diseño nos permite usar el sistema legacy a través de la nueva interfaz sin modificar el código existente.

Paso 3: Utilizar el Adapter en el Nuevo Sistema

Finalmente, veamos cómo se utiliza nuestro Adapter en el contexto del nuevo sistema.

func processPaymentInMicroservice(paymentService ModernPaymentService, amount float64, currency string) { // Crear una transacción con los datos proporcionados transaction := Transaction{Amount: amount, Currency: currency} // Ejecutar la transacción a través de la interfaz ModernPaymentService result, err := paymentService.ExecuteTransaction(transaction) if err != nil { fmt.Printf("Error processing payment: %v\n", err) return } fmt.Printf("Transaction successful: %v\n", result.Message) } func main() { // Crear una instancia del procesador de pagos legacy legacyProcessor := &LegacyPaymentProcessor{} // Crear nuestro adaptador, envolviendo el procesador legacy adapter := &PaymentServiceAdapter{legacyProcessor: legacyProcessor} // Simular una transacción en el nuevo sistema processPaymentInMicroservice(adapter, 99.99, "USD") }
  1. processPaymentInMicroservice simula cómo se procesaría un pago en el nuevo sistema de microservicios:
    • Toma un ModernPaymentService, que es la interfaz que nuestro Adapter implementa.
    • Crea una Transaction con los datos proporcionados.
    • Llama a ExecuteTransaction y maneja el resultado.
  2. En main():
    • Creamos una instancia del LegacyPaymentProcessor.
    • Creamos nuestro PaymentServiceAdapter, pasándole el procesador legacy.
    • Llamamos a processPaymentInMicroservice con el adapter, que será tratado como un ModernPaymentService.
  3. Este código demuestra cómo el nuevo sistema puede usar el procesador de pagos legacy a través del Adapter, sin saber que está interactuando con un sistema antiguo.

🔍 Análisis Profesional

  1. Encapsulación: El Adapter encapsula toda la lógica de conversión. El código cliente no necesita saber nada sobre el sistema legacy.
  2. Flexibilidad: Si en el futuro necesitamos cambiar el sistema legacy, solo necesitamos modificar el Adapter, no el código cliente.
  3. Propagación de Errores: Observa cómo propagamos los errores del sistema legacy, una práctica crucial en Go para

Vamos a recapitular los conceptos clave del patrón Adapter que acabas de dominar:

Componentes Principales del Patrón Adapter

  1. Target (Objetivo)
    • Definición: La interfaz que el cliente espera utilizar en el nuevo sistema.
    • En nuestro caso: ModernPaymentService
    • Importancia: Define el contrato que el nuevo sistema espera, facilitando la integración con sistemas modernos.
  2. Adaptee (Adaptado)
    • Definición: El componente existente con una interfaz incompatible que necesita ser integrado.
    • En nuestro caso: LegacyPaymentProcessor
    • Importancia: Representa la funcionalidad legacy que queremos preservar y utilizar en el nuevo sistema.
  3. Adapter (Adaptador)
    • Definición: La clase que implementa la interfaz Target y encapsula el Adaptee.
    • En nuestro caso: PaymentServiceAdapter
    • Importancia: Actúa como puente entre el sistema moderno y el legacy, permitiendo su interoperabilidad.

Resumen del funcionamiento del Patrón Adapter

  1. El cliente interactúa con el Adapter a través de la interfaz Target.
  2. El Adapter recibe las llamadas y las traduce en operaciones compatibles con el Adaptee.
  3. El Adaptee ejecuta la lógica de negocio legacy.
  4. El Adapter convierte el resultado en el formato esperado por el cliente moderno.

🏆 Tu Turno: Desafío del Mundo Real

Ahora que has visto cómo se aplica el patrón Adapter en un escenario real, es tu turno de practicar:

  1. Extiende el PaymentServiceAdapter para manejar múltiples divisas con diferentes tasas de conversión.
  2. Implementa logs detallados en el Adapter para facilitar el debugging en producción.

💼 Perspectiva Profesional

Como desarrollador profesional de Go, entender y aplicar patrones como el Adapter es crucial. Te permite:

  • Integrar sistemas legacy con nuevas arquitecturas de forma elegante.
  • Escribir código más mantenible y extensible.
  • Resolver problemas complejos de integración en entornos empresariales.

Recuerda, en Gognition creemos que la mejor forma de aprender es enfrentándote a problemas reales. Cada desafío que superas te acerca más a ser un Go Developer de élite. 🏅

¿Listo para más desafíos del mundo real? ¡Sigue atento a nuestros próximos posts! Y no olvides compartir tus soluciones en nuestro foro de la comunidad Gognition.

¡Hasta la próxima! 👋🐹

Comentarios

Formulario de contacto

Enviar