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

  1. 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)

  1. 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: 50

b. 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:

  1. Crea un API Gateway que maneje solicitudes para tres servicios diferentes.
  2. 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:

  1. Implementa un Circuit Breaker que permita un máximo de 3 fallos consecutivos antes de abrir el circuito.
  2. 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.

© Copyright 2024. Todos los derechos reservados