Cómo organizar un proyecto Windows Forms en C# por capas

Separar interfaz, lógica y acceso a datos para que la aplicación sea fácil de mantener, probar y hacer crecer.

Portada del artículo sobre Windows Forms por capas

Casi todos empezamos igual: arrastramos un botón al formulario, hacemos doble clic y escribimos ahí la consulta a la base de datos. Funciona, y para una prueba rápida está bien. El problema llega cuando la aplicación crece y cada cambio pequeño obliga a tocar diez formularios.

La solución clásica es dividir el proyecto en capas, cada una con una responsabilidad clara. No hace falta un framework ni una arquitectura complicada: con tres o cuatro proyectos dentro de la misma solución es suficiente.

El problema: todo dentro del formulario

Este es el código típico de un primer proyecto. La conexión, la consulta, la validación y la interfaz están mezcladas en un solo evento:

private void btnGuardar_Click(object sender, EventArgs e)
{
    if (txtNombre.Text == "") { MessageBox.Show("Falta el nombre"); return; }

    var cn = new SqlConnection("Server=.;Database=Ventas;...");
    var cmd = new SqlCommand("INSERT INTO Clientes (Nombre) VALUES ('" + txtNombre.Text + "')", cn);
    cn.Open();
    cmd.ExecuteNonQuery();
    cn.Close();
}

Tiene tres problemas serios: la cadena de conexión está repetida en cada formulario, la consulta concatena texto (lo que abre la puerta a inyección SQL) y la regla “el nombre es obligatorio” vive en la interfaz, así que hay que copiarla en cada pantalla que guarde clientes.

Las tres capas

A esas tres se suele sumar un proyecto de Entidades con las clases que viajan entre capas, como Cliente o Producto.

Diagrama de las capas: UI, Negocio y Datos

Estructura de la solución

En Visual Studio, cada capa es un proyecto distinto dentro de la misma solución. La UI es una aplicación Windows Forms y el resto son bibliotecas de clases:

MiApp.sln
├─ MiApp.UI          // Windows Forms
├─ MiApp.Negocio     // biblioteca de clases
├─ MiApp.Datos       // biblioteca de clases
└─ MiApp.Entidades   // biblioteca de clases

Las referencias van en una sola dirección: UI → Negocio → Datos, y todas conocen Entidades. La UI nunca referencia directamente a Datos.

La capa de datos

Aquí vive la cadena de conexión (leída del archivo de configuración) y las consultas, siempre con parámetros:

public class ClienteRepositorio
{
    public void Insertar(Cliente cliente)
    {
        using var cn = Conexion.Crear();
        using var cmd = new SqlCommand(
            "INSERT INTO Clientes (Nombre, Email) VALUES (@nombre, @email)", cn);
        cmd.Parameters.AddWithValue("@nombre", cliente.Nombre);
        cmd.Parameters.AddWithValue("@email", cliente.Email);
        cn.Open();
        cmd.ExecuteNonQuery();
    }
}

La capa de negocio

Las reglas se escriben una sola vez. Si algo no cumple, se lanza una excepción con un mensaje claro que la UI puede mostrar:

public class ClienteServicio
{
    private readonly ClienteRepositorio _repo = new();

    public void Registrar(Cliente cliente)
    {
        if (string.IsNullOrWhiteSpace(cliente.Nombre))
            throw new ArgumentException("El nombre es obligatorio.");

        _repo.Insertar(cliente);
    }
}

Un formulario limpio

Con todo lo anterior, el evento del botón se reduce a recoger datos, llamar al servicio y mostrar el resultado:

private void btnGuardar_Click(object sender, EventArgs e)
{
    try
    {
        _servicio.Registrar(new Cliente { Nombre = txtNombre.Text, Email = txtEmail.Text });
        MessageBox.Show("Cliente guardado.");
    }
    catch (ArgumentException ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Conclusión

Separar en capas cuesta un poco más al principio, pero se recupera rápido: cada cosa tiene su lugar, los errores son más fáciles de encontrar y la aplicación puede crecer sin volverse un laberinto.

Si estás empezando, no intentes aplicarlo todo de golpe. Empieza sacando las consultas a una clase de datos y ve moviendo las reglas a negocio poco a poco.

Enlaces y referencias

#csharp#winforms#arquitectura

Jesus_

[Una o dos líneas sobre ti: qué haces y sobre qué escribes]

// contacto