0

Arquitectura de Software con C# 27: Errores Comunes al Diseñar Arquitecturas de Software y Cómo Evitarlos

Introducción a la clase

Hemos explorado diferentes arquitecturas, sus ventajas y aplicaciones en la industria. Sin embargo, durante la implementación es común cometer errores que pueden afectar la escalabilidad, el mantenimiento y la eficiencia del software.

En esta clase aprenderás:

  • Los errores más frecuentes al aplicar Clean Architecture, Hexagonal y Vertical Slice
  • Cómo identificar y corregir problemas de diseño en la arquitectura
  • Buenas prácticas para evitar estos errores en proyectos reales

Errores Comunes en Clean Architecture

1. Acoplar la lógica de negocio con la infraestructura

  • Error: Acceder directamente a la base de datos desde los casos de uso
  • Problema: Hace que la lógica de negocio dependa de tecnologías específicas
  • Solución: Usar interfaces (puertos) y separar la infraestructura en adaptadores

Ejemplo incorrecto:

public class CrearPedidoUseCase
{
    private readonly AppDbContext _context;

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

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

Ejemplo corregido usando un puerto:

public interface IPedidoRepository
{
    void Guardar(Pedido pedido);
}

public class CrearPedidoUseCase
{
    private readonly IPedidoRepository _repository;

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

    public void Ejecutar(Pedido pedido)
    {
        _repository.Guardar(pedido);
    }
}

2. Exceso de abstracciones innecesarias

  • Error: Crear interfaces para todo, incluso cuando no es necesario
  • Problema: Hace que el código sea más difícil de leer y mantener
  • Solución: Usar abstracciones solo cuando realmente ayudan al desacoplamiento

Ejemplo innecesario:

public interface IUsuarioService
{
    void CrearUsuario(Usuario usuario);
}

public class UsuarioService : IUsuarioService
{
    public void CrearUsuario(Usuario usuario)
    {
        // Lógica de creación
    }
}

Mejor alternativa:

public class UsuarioService
{
    public void CrearUsuario(Usuario usuario)
    {
        // Lógica de creación
    }
}

Errores Comunes en Hexagonal Architecture

3. No definir correctamente los puertos (Interfaces mal diseñadas)

  • Error: Crear puertos con métodos que dependen de la infraestructura
  • Problema: No se logra el desacoplamiento total
  • Solución: Los puertos deben contener solo la lógica necesaria para la aplicación

Ejemplo incorrecto:

public interface IPedidoRepository
{
    void Guardar(Pedido pedido);
    Pedido ObtenerPorId(int id);
    string ObtenerCadenaDeConexion(); // Esto está mal, depende de la infraestructura
}

Ejemplo corregido:

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

4. Crear demasiados adaptadores innecesarios

  • Error: Cada repositorio tiene su propio adaptador aunque sean similares
  • Problema: Duplica código y hace difícil la mantención
  • Solución: Usar una estructura común para adaptadores que puedan compartir lógica

Ejemplo de adaptación innecesaria:

public class PedidoSqlRepository : IPedidoRepository { ... }
public class PedidoMongoRepository : IPedidoRepository { ... }

Mejor alternativa:

public class PedidoRepository : IPedidoRepository
{
    private readonly IDbContext _context;
    
    public PedidoRepository(IDbContext context)
    {
        _context = context;
    }

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

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

Errores Comunes en Vertical Slice Architecture

5. No reutilizar código común entre Features

  • Error: Cada Feature implementa su propia validación, lógica de autorización y acceso a datos
  • Problema: Genera código duplicado y difícil de mantener
  • Solución: Crear servicios compartidos o middleware para lógica repetitiva

Ejemplo incorrecto:

public class CrearPedidoHandler
{
    public void Handle(CrearPedidoCommand command)
    {
        if (command.Cliente == "")
            throw new Exception("Cliente requerido");

        if (!command.Productos.Any())
            throw new Exception("Debe incluir productos");

        // Lógica de creación...
    }
}

Ejemplo corregido con validación reutilizable:

public class CrearPedidoValidator : AbstractValidator<CrearPedidoCommand>
{
    public CrearPedidoValidator()
    {
        RuleFor(x => x.Cliente).NotEmpty();
        RuleFor(x => x.Productos).NotEmpty();
    }
}

6. No definir una estructura clara de Features

  • Error: Features mezcladas sin estructura clara
  • Problema: A medida que crece el proyecto, es difícil encontrar código relacionado
  • Solución: Mantener una estructura consistente de Features

Ejemplo incorrecto:

/Features
    CrearPedidoCommand.cs
    ObtenerPedidoQuery.cs
    PedidoRepository.cs

Ejemplo corregido:

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

Errores Comunes Generales en Arquitectura de Software

7. No usar inyección de dependencias correctamente

  • Error: Crear instancias manualmente en el código en lugar de usar un contenedor de IoC
  • Problema: Hace que el código sea rígido y difícil de testear
  • Solución: Usar inyección de dependencias

Ejemplo incorrecto:

public class PedidoService
{
    private readonly PedidoRepository _repository = new PedidoRepository();
}

Ejemplo corregido con inyección de dependencias:

public class PedidoService
{
    private readonly IPedidoRepository _repository;

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

8. No realizar pruebas unitarias en la lógica de negocio

  • Error: Probar solo controladores y no la lógica interna
  • Problema: Si el código cambia, no hay garantías de que siga funcionando correctamente
  • Solución: Implementar pruebas unitarias en la lógica del dominio

Ejemplo de prueba de un agregado:

[Fact]
public void PedidoDebeCalcularTotalCorrectamente()
{
    var pedido = new Pedido(1, "Cliente1");
    pedido.AgregarProducto(new Producto(1, "Producto1", 100));
    pedido.AgregarProducto(new Producto(2, "Producto2", 200));

    Assert.Equal(300, pedido.CalcularTotal());
}

Cuestionario de Autoevaluación

  • ¿Por qué es importante separar la lógica de negocio de la infraestructura?
  • ¿Cuándo una abstracción es innecesaria en Clean Architecture?
  • ¿Cómo asegurarte de que un puerto en Hexagonal Architecture esté bien diseñado?
  • ¿Qué estrategia puedes usar en Vertical Slice Architecture para evitar código duplicado?
  • ¿Por qué es recomendable usar validaciones centralizadas en Vertical Slice Architecture?

Resumen de la Clase

  • Evitar acoplar la lógica de negocio con la infraestructura
  • No crear abstracciones innecesarias que complican el mantenimiento
  • Diseñar correctamente los puertos y adaptadores en Hexagonal Architecture
  • Mantener una estructura clara y evitar código duplicado en Vertical Slice Architecture
  • Usar inyección de dependencias y pruebas unitarias para garantizar la calidad del código

Próximo paso

En la siguiente clase veremos Patrones avanzados y combinaciones entre arquitecturas para construir sistemas escalables y flexibles.

Fernando Sonego

Deja una respuesta

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