0

Arquitectura de Software con C# 28: Patrones Avanzados y Combinaciones Entre Arquitecturas

Introducción a la clase

Hemos explorado Clean Architecture, Hexagonal Architecture y Vertical Slice Architecture, sus beneficios y errores comunes. En esta clase aprenderemos cómo combinar estas arquitecturas y aplicar patrones avanzados para construir sistemas escalables, flexibles y bien estructurados.

En esta clase aprenderás:

  • Cómo combinar Clean, Hexagonal y Vertical Slice Architecture en un mismo proyecto
  • Patrones avanzados como Event Sourcing, CQRS y Domain Events
  • Cómo aplicar estos patrones en la práctica con C#

Combinaciones de Arquitecturas en Proyectos Reales

1. Clean Architecture + CQRS + Event Sourcing

  • Se usa Clean Architecture para estructurar la aplicación en capas bien definidas
  • CQRS separa operaciones de lectura y escritura
  • Event Sourcing almacena los cambios como eventos inmutables

Ejemplo de estructura:

/Core
    /Domain
        Pedido.cs
        PedidoCreadoEvent.cs
    /Application
        /Commands
            CrearPedidoCommand.cs
            CrearPedidoHandler.cs
        /Queries
            ObtenerPedidoQuery.cs
            ObtenerPedidoHandler.cs
    /Infrastructure
        PedidoRepository.cs
    /Presentation
        PedidoController.cs

Ejemplo de Evento de Dominio:

public class PedidoCreadoEvent
{
    public int PedidoId { get; }
    public string Cliente { get; }

    public PedidoCreadoEvent(int pedidoId, string cliente)
    {
        PedidoId = pedidoId;
        Cliente = cliente;
    }
}

Beneficios de esta combinación:

  • Alta escalabilidad → CQRS mejora la performance separando lectura/escritura
  • Auditabilidad completa → Event Sourcing permite reconstruir el estado del sistema
  • Reglas de negocio bien organizadas → Clean Architecture separa responsabilidades

2. Hexagonal Architecture + DDD + Microservicios

  • Hexagonal Architecture desacopla completamente el dominio de la infraestructura
  • DDD se usa para modelar la lógica de negocio con Agregados y Entidades bien definidas
  • Cada microservicio implementa su propio núcleo hexagonal

Ejemplo de estructura en microservicios:

/PedidosService
    /Core
        Pedido.cs
        PedidoService.cs
    /Infrastructure
        PedidoRepository.cs
    /Adapters
        PedidoController.cs
        PedidoEventHandler.cs

Ejemplo de Puerto y Adaptador:

// Puerto
public interface IPedidoRepository
{
    void Guardar(Pedido pedido);
    Pedido ObtenerPorId(int id);
}

// Adaptador
public class PedidoRepository : IPedidoRepository
{
    private readonly AppDbContext _context;

    public PedidoRepository(AppDbContext context)
    {
        _context = context;
    }

    public void Guardar(Pedido pedido)
    {
        _context.Pedidos.Add(pedido);
        _context.SaveChanges();
    }

    public Pedido ObtenerPorId(int id)
    {
        return _context.Pedidos.FirstOrDefault(p => p.Id == id);
    }
}

Beneficios de esta combinación:

  • Independencia de la infraestructura → Fácil cambiar bases de datos o APIs externas
  • Escalabilidad → Cada microservicio es completamente independiente
  • Código modular → Cada servicio encapsula su propio dominio

3. Vertical Slice Architecture + MediatR + CQRS

  • Vertical Slice divide la aplicación en módulos independientes por Feature
  • MediatR maneja Commands y Queries sin necesidad de una capa de servicios
  • CQRS separa operaciones de lectura y escritura

Ejemplo de estructura:

/Features
    /CrearPedido
        CrearPedidoCommand.cs
        CrearPedidoHandler.cs
        PedidoRepository.cs
    /ObtenerPedido
        ObtenerPedidoQuery.cs
        ObtenerPedidoHandler.cs

Ejemplo de Handler con MediatR:

public class CrearPedidoHandler : IRequestHandler<CrearPedidoCommand, int>
{
    private readonly IPedidoRepository _repository;

    public CrearPedidoHandler(IPedidoRepository repository)
    {
        _repository = repository;
    }

    public async Task<int> Handle(CrearPedidoCommand request, CancellationToken cancellationToken)
    {
        var pedido = new Pedido(0, request.Cliente);
        _repository.Guardar(pedido);
        return await Task.FromResult(pedido.Id);
    }
}

Beneficios de esta combinación:

  • Desarrollo rápido y modular → Cada Feature es completamente independiente
  • Menos dependencias → MediatR elimina la necesidad de inyectar servicios en controladores
  • Escalabilidad extrema → Fácil agregar nuevas Features sin modificar las existentes

Patrones Avanzados para Arquitecturas Escalables

1. Event Sourcing

  • En lugar de guardar solo el estado actual, se almacenan todos los eventos que han ocurrido
  • Permite auditoría completa y la reconstrucción del estado del sistema

Ejemplo de almacenamiento de eventos:

PedidoCreado (ID: 1, Cliente: "Juan")
ProductoAgregado (PedidoID: 1, Producto: "Laptop")
PagoRealizado (PedidoID: 1, Monto: 1200 USD)

Beneficios:

  • Historial completo de cambios
  • Facilita la depuración y auditoría
  • Ideal para sistemas bancarios y logísticos

2. CQRS (Command Query Responsibility Segregation)

  • Se separan las operaciones de lectura (Queries) y escritura (Commands)
  • Mejora la performance y la escalabilidad

Ejemplo de separación:

public class CrearPedidoCommand : IRequest<int> { ... }
public class ObtenerPedidoQuery : IRequest<Pedido> { ... }

Beneficios:

  • Optimiza el rendimiento → Consultas rápidas sin afectar operaciones de escritura
  • Mejor organización del código

3. Domain Events

  • Permite notificar a otros módulos cuando ocurre un evento en el dominio
  • Se usa para desacoplar módulos sin necesidad de dependencias directas

Ejemplo de publicación de un evento:

public class PedidoCreadoEvent : INotification
{
    public int PedidoId { get; }
    public PedidoCreadoEvent(int pedidoId) { PedidoId = pedidoId; }
}

Ejemplo de suscriptor del evento:

public class NotificarClienteHandler : INotificationHandler<PedidoCreadoEvent>
{
    public Task Handle(PedidoCreadoEvent notification, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Notificación enviada al cliente para el pedido {notification.PedidoId}");
        return Task.CompletedTask;
    }
}

Beneficios:

  • Menos acoplamiento → Los módulos se comunican sin referencias directas
  • Facilita la escalabilidad → Se pueden agregar nuevos listeners sin modificar código existente

Cuestionario de Autoevaluación

  • ¿Cómo se pueden combinar Clean Architecture y CQRS en un proyecto real?
  • ¿Por qué Hexagonal Architecture es una buena opción para microservicios?
  • ¿Qué ventajas tiene Event Sourcing sobre una base de datos tradicional?
  • ¿Cómo puedes evitar la duplicación de código en Vertical Slice Architecture?
  • ¿Cuándo deberías usar Domain Events en un sistema distribuido?

Resumen de la Clase

  • Se pueden combinar diferentes arquitecturas para maximizar la escalabilidad y el mantenimiento del código
  • Event Sourcing permite almacenar el historial de eventos para reconstrucción del estado
  • CQRS separa lectura y escritura para mejorar el rendimiento
  • Domain Events ayudan a desacoplar módulos en sistemas distribuidos

Próximo paso

En la siguiente y última clase haremos un cierre del curso, revisión de los conceptos clave y un roadmap para seguir aprendiendo.

Fernando Sonego

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *