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.