Chain of Responsibility en servicios de pagos
Clase 26 de 27 • Curso de Patrones de Diseño y SOLID en Python
Contenido del curso
Principios SOLID
- 2

Principio de responsabilidad única en SOLID
05:59 min - 3

Refactorizando código Python con principios SOLID
11:14 min - 4

Cómo aplicar SRP en un procesador de pagos con Stripe
25:19 min - 5

Open Closed Principle: extensión sin modificación
02:39 min - 6

Cómo usar clases abstractas en Python
14:46 min - 7

Principio de Liskov en S.O.L.I.D.
03:18 min - 8

Principio de sustitución de Liskov en Python
06:38 min - 9

Interface Segregation: cuándo separar contratos
02:33 min - 10

Segregación de interfaces en procesadores de pagos
09:05 min - 11

Principio de inversión de dependencias explicado
04:13 min - 12

Principio de inversión de dependencias: servicio de pagos flexible
05:56 min
Reestructuración del proyecto
Patrones de Diseño
- 14

Qué son los patrones de diseño: definición y categorías
03:54 min - 15

Strategy Pattern con Python y setprocessor
01:55 min - 16

Strategy Pattern para pagos en Python
10:58 min - 17

Factory Pattern: centralizar creación de objetos
03:05 min - 18

Patrón Factory para procesar pagos con match
11:06 min - 19

Patrón Decorator en 5 pasos para funcionalidad dinámica
03:06 min - 20

Patrón decorador en servicios de pagos
12:57 min - 21

Builder Pattern: construcción paso a paso
01:28 min - 22

Builder pattern para servicios de pagos
18:55 min - 23

Observer Pattern en sistemas de eventos
01:48 min - 24

Observer en sistemas de pagos con Python
11:11 min - 25

Chain of Responsibility para validar pagos
02:04 min - 26

Chain of Responsibility en servicios de pagos
Viendo ahora - 27

Arquitectura robusta para procesadores de pago
03:19 min
Aplicar el patrón Chain of Responsibility a un servicio de pagos en Python mejora la organización de validaciones y facilita la extensión. Aquí verás cómo encapsular datos con Pydantic, crear un chain handler abstracto con ABC y dataclass, implementar un CustomerHandler y ajustar el servicio y el builder para validar en cadena.
¿Cómo estructurar el request y por qué usar Pydantic?
Encapsular en un solo objeto los datos que participan en la validación simplifica la firma de métodos y estandariza la entrada. Se define una clase request que hereda de BaseModel para asegurar tipos y validación básica.
- Encapsulación en un único request para customer y payment.
- Uso de Pydantic BaseModel: tipado fuerte y validación declarativa.
- Menos acoplamiento: validadores reciben el mismo contrato.
from pydantic import BaseModel
# Declarada en el módulo commons
class Request(BaseModel):
customer_data: CustomerData
payment_data: PaymentData
¿Cómo definir el chain handler abstracto con ABC y dataclass?
La cadena requiere un eslabón común con comportamiento compartido: establecer el siguiente handler y un método handle obligado. Se combina ABC para el contrato, dataclass para el atributo next_handler, y Self para tipos recursivos. El método set_next retorna el propio siguiente eslabón para encadenar con fluidez.
- Clase abstracta con
@abstractmethodparahandle. - Atributo next_handler opcional para el siguiente eslabón.
- set_next uniforme en toda la cadena.
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Optional, Self
from commons import Request
@dataclass
class ChainHandler(ABC):
next_handler: Optional[Self] = None
def set_next(self, handler: Self) -> Self:
self.next_handler = handler
return handler
@abstractmethod
def handle(self, request: Request) -> None:
...
¿Cómo implementar CustomerHandler y conectar la cadena en el builder?
Este eslabón valida el customer con un validador existente. Si todo es correcto, delega al siguiente handler. Si ocurre un error, lo propaga. Se usa try/except porque el validador lanza excepciones cuando hay datos inválidos.
- Delegación al siguiente eslabón solo si existe
next_handler. - Propagación de excepciones para detener el flujo ante fallas.
- Separación de responsabilidades: cada handler valida una cosa.
from commons import Request
from validators.handler import ChainHandler
from Customer import CustomerValidator # Validador existente
class CustomerHandler(ChainHandler):
def handle(self, request: Request) -> None:
validator = CustomerValidator()
try:
validator.validate(request.customer_data)
if self.next_handler:
self.next_handler.handle(request)
except Exception as exc:
print("Fallo en validación de customer.")
raise exc
¿Cómo se adapta el servicio y el protocolo?
El servicio deja de depender de múltiples validadores y recibe un único ChainHandler. Construye el request y ejecuta la cadena. Ante error, imprime y relanza la excepción para frenar el proceso.
from commons import Request
# Dentro del servicio
req = Request(customer_data=customer_data, payment_data=payment_data)
try:
self.validator.handle(req)
except Exception as exc:
print("Fallo en las validaciones.")
raise exc
- Protocolo actualizado: depende de ChainHandler, no de una lista de validadores.
- Implementación alineada: un único punto de entrada
handle. - Flujo claro: construir
Requesty ejecutar la cadena.
¿Cómo configurar la cadena en el builder?
El builder expone un método para armar la cadena. El primer eslabón se guarda como self.validator. El método retorna self para encadenar configuraciones y el build inyecta el validador en el servicio.
from typing import Optional, Self
from validators.handler import ChainHandler
from validators.customer_handler import CustomerHandler
class PaymentServiceBuilder:
validator: Optional[ChainHandler] = None
def set_chain_of_validations(self) -> Self:
ch1 = CustomerHandler()
ch2 = CustomerHandler() # Ejemplo: encadenar otro eslabón
ch1.set_next(ch2)
self.validator = ch1
return self
def build(self) -> PaymentService:
return PaymentService(validator=self.validator)
- Método fluido: retorna
selfpara continuar la configuración. - Primer eslabón: punto de entrada de la cadena.
- Opcionalidad:
validatorpuede serNonehasta configurarlo.
¿Cómo implementarías el handler para payment_data y cómo encadenarías toda la validación en el builder? Comparte tu enfoque y decisiones en los comentarios.