En este módulo, exploraremos cómo los patrones de diseño se aplican en arquitecturas modernas, como la arquitectura de microservicios, sistemas distribuidos y desarrollo ágil. Estos patrones ayudan a resolver problemas comunes y a mejorar la eficiencia y la escalabilidad de las aplicaciones modernas.
Introducción
Las arquitecturas modernas han evolucionado para enfrentar los desafíos de escalabilidad, flexibilidad y mantenimiento en el desarrollo de software. Los patrones de diseño juegan un papel crucial en estas arquitecturas, proporcionando soluciones probadas y reutilizables para problemas comunes.
Objetivos de este tema:
- Comprender la importancia de los patrones de diseño en arquitecturas modernas.
- Explorar ejemplos específicos de patrones aplicados en estas arquitecturas.
- Proporcionar ejercicios prácticos para reforzar el aprendizaje.
Patrones de Diseño en Arquitecturas Modernas
- Patrones en Arquitectura de Microservicios
La arquitectura de microservicios divide una aplicación en servicios pequeños e independientes que se comunican entre sí. Algunos patrones comunes en esta arquitectura incluyen:
a. API Gateway
El patrón API Gateway actúa como un punto de entrada único para todas las solicitudes de clientes. Maneja la enrutación, la composición y la traducción de protocolos.
Ejemplo:
# Ejemplo de un API Gateway en Python usando Flask
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/service1', methods=['GET'])
def service1():
# Lógica para el servicio 1
return jsonify({"message": "Response from Service 1"})
@app.route('/service2', methods=['GET'])
def service2():
# Lógica para el servicio 2
return jsonify({"message": "Response from Service 2"})
if __name__ == '__main__':
app.run(port=5000)b. Circuit Breaker
El patrón Circuit Breaker evita que una aplicación intente realizar una operación que probablemente fallará, mejorando la resiliencia del sistema.
Ejemplo:
# Ejemplo de un Circuit Breaker en Python
class CircuitBreaker:
def __init__(self, failure_threshold, recovery_timeout):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.failure_count = 0
self.last_failure_time = None
def call(self, func, *args, **kwargs):
if self.failure_count >= self.failure_threshold:
if (time.time() - self.last_failure_time) < self.recovery_timeout:
raise Exception("Circuit Breaker is open")
else:
self.failure_count = 0
try:
result = func(*args, **kwargs)
self.failure_count = 0
return result
except Exception as e:
self.failure_count += 1
self.last_failure_time = time.time()
raise e
# Uso del Circuit Breaker
import time
def unreliable_service():
if time.time() % 2 == 0:
raise Exception("Service failed")
return "Service succeeded"
cb = CircuitBreaker(failure_threshold=3, recovery_timeout=5)
try:
print(cb.call(unreliable_service))
except Exception as e:
print(e)
- Patrones en Sistemas Distribuidos
Los sistemas distribuidos requieren patrones que aborden la comunicación, la consistencia y la tolerancia a fallos.
a. Event Sourcing
El patrón Event Sourcing almacena el estado de una aplicación como una secuencia de eventos, lo que permite reconstruir el estado actual a partir de estos eventos.
Ejemplo:
# Ejemplo de Event Sourcing en Python
class EventStore:
def __init__(self):
self.events = []
def save_event(self, event):
self.events.append(event)
def get_events(self):
return self.events
class Account:
def __init__(self, event_store):
self.event_store = event_store
self.balance = 0
def apply_event(self, event):
if event['type'] == 'deposit':
self.balance += event['amount']
elif event['type'] == 'withdraw':
self.balance -= event['amount']
def deposit(self, amount):
event = {'type': 'deposit', 'amount': amount}
self.event_store.save_event(event)
self.apply_event(event)
def withdraw(self, amount):
event = {'type': 'withdraw', 'amount': amount}
self.event_store.save_event(event)
self.apply_event(event)
def get_balance(self):
return self.balance
# Uso de Event Sourcing
event_store = EventStore()
account = Account(event_store)
account.deposit(100)
account.withdraw(50)
print(account.get_balance()) # Output: 50b. CQRS (Command Query Responsibility Segregation)
El patrón CQRS separa las operaciones de lectura y escritura en diferentes modelos, optimizando el rendimiento y la escalabilidad.
Ejemplo:
# Ejemplo de CQRS en Python
class CommandHandler:
def __init__(self, event_store):
self.event_store = event_store
def handle(self, command):
if command['type'] == 'create_account':
event = {'type': 'account_created', 'account_id': command['account_id']}
self.event_store.save_event(event)
class QueryHandler:
def __init__(self, event_store):
self.event_store = event_store
def get_account_events(self, account_id):
return [event for event in self.event_store.get_events() if event.get('account_id') == account_id]
# Uso de CQRS
event_store = EventStore()
command_handler = CommandHandler(event_store)
query_handler = QueryHandler(event_store)
command_handler.handle({'type': 'create_account', 'account_id': 1})
print(query_handler.get_account_events(1)) # Output: [{'type': 'account_created', 'account_id': 1}]Ejercicios Prácticos
Ejercicio 1: Implementar un API Gateway
Instrucciones:
- Crea un API Gateway que maneje solicitudes para tres servicios diferentes.
- Cada servicio debe devolver un mensaje diferente.
Solución:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/service1', methods=['GET'])
def service1():
return jsonify({"message": "Response from Service 1"})
@app.route('/service2', methods=['GET'])
def service2():
return jsonify({"message": "Response from Service 2"})
@app.route('/service3', methods=['GET'])
def service3():
return jsonify({"message": "Response from Service 3"})
if __name__ == '__main__':
app.run(port=5000)Ejercicio 2: Implementar un Circuit Breaker
Instrucciones:
- Implementa un Circuit Breaker que permita un máximo de 3 fallos consecutivos antes de abrir el circuito.
- El circuito debe permanecer abierto durante 5 segundos antes de intentar restablecerse.
Solución:
import time
class CircuitBreaker:
def __init__(self, failure_threshold, recovery_timeout):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.failure_count = 0
self.last_failure_time = None
def call(self, func, *args, **kwargs):
if self.failure_count >= self.failure_threshold:
if (time.time() - self.last_failure_time) < self.recovery_timeout:
raise Exception("Circuit Breaker is open")
else:
self.failure_count = 0
try:
result = func(*args, **kwargs)
self.failure_count = 0
return result
except Exception as e:
self.failure_count += 1
self.last_failure_time = time.time()
raise e
def unreliable_service():
if time.time() % 2 == 0:
raise Exception("Service failed")
return "Service succeeded"
cb = CircuitBreaker(failure_threshold=3, recovery_timeout=5)
try:
print(cb.call(unreliable_service))
except Exception as e:
print(e)Conclusión
En este tema, hemos explorado cómo los patrones de diseño se aplican en arquitecturas modernas como microservicios y sistemas distribuidos. Los patrones como API Gateway, Circuit Breaker, Event Sourcing y CQRS son esenciales para construir aplicaciones escalables y resilientes. A través de ejemplos prácticos y ejercicios, hemos reforzado la comprensión de estos patrones y su implementación.
En el siguiente tema, profundizaremos en los patrones de diseño en microservicios, explorando más patrones y sus aplicaciones prácticas.
Curso de Patrones de Diseño de Software
Módulo 1: Introducción a los Patrones de Diseño
- ¿Qué son los Patrones de Diseño?
- Historia y Origen de los Patrones de Diseño
- Clasificación de los Patrones de Diseño
- Ventajas y Desventajas de Usar Patrones de Diseño
Módulo 2: Patrones Creacionales
- Introducción a los Patrones Creacionales
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Prototype
Módulo 3: Patrones Estructurales
Módulo 4: Patrones de Comportamiento
- Introducción a los Patrones de Comportamiento
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Módulo 5: Aplicación de Patrones de Diseño
- Cómo Seleccionar el Patrón Adecuado
- Ejemplos Prácticos de Uso de Patrones
- Patrones de Diseño en Proyectos Reales
- Refactorización Usando Patrones de Diseño
Módulo 6: Patrones de Diseño Avanzados
- Patrones de Diseño en Arquitecturas Modernas
- Patrones de Diseño en Microservicios
- Patrones de Diseño en Sistemas Distribuidos
- Patrones de Diseño en Desarrollo Ágil
