Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Programación en 3 Capas
Programación en 3 Capas
Hola a todos:
Después de tantos meses de tener abandonado el Blog por fin hoy se libera un espacio en mi
agenda, tiempo que he decidido compartir con todos y cada uno de ustedes.
En este articulo hablare y tratare de explicar con los detalles mas mínimos que es la
arquitectura 3 capas, cuales son sus ventajas, como empezar un proyecto 3 capas, cuales son
las diferencias entre cada una de ellas, como comunicarlas y como crear un proyecto con la
arquitectura 3 capas utilizando Visual Studio 2012.
Antes de comenzar a leer este articulo recuerde que: “El objetivo no es otro mas que el de
orientar a los Parvulos .Net sobre la arquitectura de software 3 capas, todo lo escrito en este
articulo no es ensayado, no es revisado por nadie mas, por lo cual podría contener errores
gramaticales y sintácticos, el articulo y sus conceptos no pretenden ser la verdad absoluta del
tema, siéntase en confianza de dejar sus comentarios y opiniones en la sección de comentarios
al final del mismo y si lo considera prudente envíeme un correo electrónico por medio del
formulario de contacto y por ultimo si el articulo le es de utilidad por favor considere dejar un
comentario de agradecimiento.”
Requisitos: Visual Studio 2012, Framework 4.0, el proveedor de datos SqlCompact 4.0
instalado en su equipo y muchas ganas de aprender.
Como siempre recomiendo encarecidamente que antes de descargar los proyectos de ejemplo
(que les pondré al final de articulo), traten de hacerlo ustedes mismos siguiendo paso a paso
todo lo que se mencionara aquí, si tienen dudas en uno en especifico no duden en
contactarme.
¿Ha creado usted software?…¿Si?, entonces sabe lo complejo que resulta crear rutinas y
funciones para cada uno de los formularios, importar todas las referencias del motor de base
de datos en cada uno de los formularios, cambiar código aquí y allá (porque tiene copias del
mismo código en muchos lugares), escribir la lógica de validación de campos dentro del
evento, corregir los bugs que pudieran presentarse y no se diga de implementarles mejoras al
software, etc., ¿No? entonces no se preocupe este es un buen manual de como evitarse
muchos dolores de cabeza en diseño de su arquitectura a seguir.
Para que se de una mejor idea de que hablo, por favor descargue este proyecto de ejemplo,
ejecútelo, analice el código, observe como para realizar una misma funcionalidad en dos
lugares diferentes tuvimos que escribir casi las mismas líneas de código.
Hasta aquí, hemos visto la teoría de lo que representa la Arquitectura 3 Capas, pero…
2. Diríjase al “Explorador de soluciones” y haga click derecho sobre el proyecto que acabamos
de crear:
3. Del menú desplegado seleccione Agregar->Nuevo Proyecto:
4. Seleccione, Instalado –> Visual C# –> Windows –> Aplicación de Windows Forms –> En el
campo Nombre escriba, “Tienda-Presentacion” y presione el botón “Aceptar”.
Observe que cuando creamos el proyecto principal en automático se creo un proyecto vacío,
eliminemos ese proyecto para dejar la siguiente estructura:
Ahora ya tenemos nuestra estructura completa, observe que creamos 4 proyectos dentro del
principal (que lo iniciamos como proyecto vacío), recuerde que el proyecto de Entidades en
este caso “Tienda-Entidades” no es una capa, mas bien, complementa a la capa de Lógica de
Negocio.
Capa de Entidades
Recuerde que en esta capa estarán alojados los objetos (clases) con los cuales estaremos
trabajando, con estos objetos estaremos persistiendo las tablas de la base de datos que
utilizaremos.
La base de datos que usaremos contiene una sola tabla llamada “Producto” la cual contiene 5
campos (Id ‘int’, Descripción ‘nvarchar’, Marca ‘nvarchar’, Precio ‘nvarchar’), por lo cual
la Capa de Entidades contendrá un Objeto llamado Producto con 5 propiedades publicas cada
uno de ellos respetando el tipo de dato con el cual esta declarado en la base de datos.
1. Agregue una clase al proyecto “Tienda-Entidades” y llámela “EProducto” (La letra ‘E’ es por
conveción, es decir, todos los objetos de nuestra capa de Entidades llevaran la letra ‘E’ al
principio para que desde donde las lleguemos a utilizar sepamos que ese Objeto es una
Entidad evitando con esto confusiones con otros objetos), dentro agregue las siguientes líneas
de código:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tienda_Entidades
{
public class EProducto
{
public int Id { get; set; }
public string Descripcion { get; set; }
public string Marca { get; set; }
public decimal Precio { get; set; }
}
}
Si tuviéramos mas tablas en nuestra base de datos tendríamos que crear la misma cantidad de
objetos en nuestra capa de Entidades y dentro contendrían la misma cantidad de propiedades
como campos tiene la tabla siempre respetando el tipo de dato con lo que estos campos están
declarados.
- Click derecho sobre el proyecto “Tienda-AccesoDatos –> Agregar –> Nuevo elemento
2. En el proyecto que descargaron anteriormente si observaron viene embebida un archivo de
base de datos llamado DataBase1.sdf que noes otra cosa mas que un archivo de base de
datos propios del motor SQlCompact, cree una carpeta en la unidad C y llámela “Proyecto-
3Capas”, después copien este archivo en ese path.
Observen que el DataSource apunta a la carpeta que acabamos de crear en la unidad C “Data
Source=C:\Proyecto-3Capas\Database1.sdf", esta cadena en proyetos reales normalmente
apuntara a un servidor de datos, pero para fines ilustrativos, este directorio nos servirá muy
bien.
4. Agreguemos las referencias a las librerías System.Configuration para esto, Click sobre
nuestra capa de datos (proyecto “Tienda-AccesoDatos”) –> Agregar Referencia
5. Agregue una segunda referencia ahora a System.Data.SqlServeCe (esta librería no siempre
aparece, si este es su caso presione el botón Examinar localice la Dll dentro de archivos de
programa y selecciónela), presione Aceptar.
Como nuestra capa de Acceso a Datos, debe de tener la capacidad de Insertar, Leer,
Actualizar y Eliminar registros de la tabla Productos, requiere de una comunicación directa con
la capa de Entidades, para por ejemplo, al momento de que se le solicite la inserción de un
Producto en lugar de enviarle 5 parámetros con los datos del producto se le envié un Objeto y
¿Cual será este objeto? nada mas y nada menos que nuestro objeto EProductocreado en la
capa de Entidades, de esta manera ya no trabajaremos con datos sino con objetos llenando,
enviando y leyendo propiedades.
Observe como se a creado un nuevo elemento en nuestra carpeta de Referencias del proyecto
“Tienda-AccesoDatos”
9. Inserte una clase nueva llamada “ProductoDal” de donde Dal vendrá de Data Access Layer,
dentro de la clase que acaba de crear tiene que declarar el espacio de
nombres System.Configuration, System.Data.SqlServerCe y del proyecto de Entidades,
además tiene que declarar la clase como publica (para que la Capa de Lógica de Negocio
pueda tener acceso a ella)
Para hacer esta tarea mas sencilla le proporcionare el código que deberá de poner en la clase
“ProductoDal”, pero, trate de escribir el código para vaya analizando la estructura del mismo.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//nuestras importaciones del Espacio de nombres que estaremos
utilizando,
//recuerde que estas son las referencias que realizamos hace unos
momentos...
using System.Configuration;
using System.Data.SqlServerCe;
using Tienda_Entidades;
namespace Tienda_AccesoDatos
{
//Definimos el acceso de nuestra clase como public, asegurando con
esto su accesibilidad desde
//otros proyectos.
public class ProductoDal
{
//Primero y siguiendo el orden de las acciones CRUD
//Crearemos un Método que se encarga de insertar un nuevo
Producto es nuestra tabla Producto
/// <summary>
/// Inserta un nuevo Producto en la tabla Producto
/// </summary>
/// <param name="producto">Entidad contenedora de los valores
a insertar</param>
/// <autor>José Luis García Bautista</autor>
public void Insert(EProducto producto)
{
//Creamos nuestro objeto de conexion usando nuestro
archivo de configuraciones
using (SqlCeConnection cnx = new
SqlCeConnection(ConfigurationManager.ConnectionStrings["cnnString"].To
String()))
{
cnx.Open();
//Declaramos nuestra consulta de Acción Sql
parametrizada
const string sqlQuery =
"INSERT INTO Producto (Descripcion, Marca, Precio)
VALUES (@descripcion, @marca, @precio)";
using (SqlCeCommand cmd = new SqlCeCommand(sqlQuery,
cnx))
{
//El primero de los cambios significativos con
respecto al ejemplo descargado es que aqui...
//ya no leeremos controles sino usaremos las
propiedades del Objeto EProducto de nuestra capa
//de entidades...
cmd.Parameters.AddWithValue("@descripcion",
producto.Descripcion);
cmd.Parameters.AddWithValue("@marca",
producto.Marca);
cmd.Parameters.AddWithValue("@precio",
producto.Precio);
cmd.ExecuteNonQuery();
}
}
}
/// <summary>
/// Devuelve una lista de Productos ordenados por el campo Id
de manera Ascendente
/// </summary>
/// <returns>Lista de productos</returns>
/// <autor>José Luis García Bautista</autor>
public List<EProducto> GetAll()
{
//Declaramos una lista del objeto EProducto la cual será
la encargada de
//regresar una colección de los elementos que se obtengan
de la BD
//
//La lista substituye a DataTable utilizado en el proyecto
de ejemplo
List<EProducto> productos = new List<EProducto>();
/// <summary>
/// Devuelve un Objeto Producto
/// </summary>
/// <param name="idProducto">Id del producto a buscar</param>
/// <returns>Un registro con los valores del
Producto</returns>
/// <autor>José Luis García Bautista</autor>
public EProducto GetByid(int idProducto)
{
using (SqlCeConnection cnx = new
SqlCeConnection(ConfigurationManager.ConnectionStrings["cnnString"].To
String()))
{
cnx.Open();
return producto;
}
}
}
return null;
}
/// <summary>
/// Actualiza el Producto correspondiente al Id proporcionado
/// </summary>
/// <param name="producto">Valores utilizados para hacer el
Update al registro</param>
/// <autor>José Luis García Bautista</autor>
public void Update(EProducto producto)
{
using (SqlCeConnection cnx = new
SqlCeConnection(ConfigurationManager.ConnectionStrings["cnnString"].To
String()))
{
cnx.Open();
const string sqlQuery =
"UPDATE Producto SET Descripcion = @descripcion,
Marca = @marca, Precio = @precio WHERE Id = @id";
using (SqlCeCommand cmd = new SqlCeCommand(sqlQuery,
cnx))
{
cmd.Parameters.AddWithValue("@descripcion",
producto.Descripcion);
cmd.Parameters.AddWithValue("@marca",
producto.Marca);
cmd.Parameters.AddWithValue("@precio",
producto.Precio);
cmd.Parameters.AddWithValue("@id", producto.Id);
cmd.ExecuteNonQuery();
}
}
}
/// <summary>
/// Elimina un registro coincidente con el Id Proporcionado
/// </summary>
/// <param name="idproducto">Id del registro a
Eliminar</param>
/// <autor>José Luis García Bautista</autor>
public void Delete(int idproducto)
{
using (SqlCeConnection cnx = new
SqlCeConnection(ConfigurationManager.ConnectionStrings["cnnString"].To
String()))
{
cnx.Open();
const string sqlQuery = "DELETE FROM Producto WHERE Id
= @id";
using (SqlCeCommand cmd = new SqlCeCommand(sqlQuery,
cnx))
{
cmd.Parameters.AddWithValue("@id", idproducto);
cmd.ExecuteNonQuery();
}
}
}
}
}
Observe como desde nuestros diferentes Métodos y funciones estamos haciendo uso del
objeto EProductodeclarado en la capa de Entidades…Observe también como nuestra
clase ProductosDal no valida que el valor de las propiedades de EProducto contengan datos o
sean del tipo de dato correcto porque ese ¿es trabajo de?…La Capa de Lógica de Negocio
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//
//Hacemos las importaciones del espacio de nombres de los dos
proyectos que referenciamos
//observe como esta capa solo referencio a Tienda-AccessData y no a
Tienda-Presentacion
//observe también como aquí no es requerida la referencia a
System.Data.SqlServerCe
using Tienda_AccesoDatos;
using Tienda_Entidades;
namespace Tienda_LogicaNegocio
{
public class ProductoBol
{
//Instanciamos nuestra clase ProductoDal para poder utilizar
sus miembros
private ProductoDal _productoDal = new ProductoDal();
//
//El uso de la clase StringBuilder nos ayudara a devolver los
mensajes de las validaciones
public readonly StringBuilder stringBuilder = new
StringBuilder();
//
//Creamos nuestro método para Insertar un nuevo Producto,
observe como este método tampoco valida los el contenido
//de las propiedades, sino que manda a llamar a una Función
que tiene como tarea única hacer esta validación
//
public void Registrar(EProducto producto)
{
if(ValidarProducto(producto))
{
if (_productoDal.GetByid(producto.Id) == null)
{
_productoDal.Insert(producto);
}
else
_productoDal.Update(producto);
}
}
if(stringBuilder.Length == 0)
{
return _productoDal.GetByid(idProduct);
}
return null;
}
if (stringBuilder.Length == 0)
{
_productoDal.Delete(idProduct);
}
}
if (string.IsNullOrEmpty(producto.Descripcion))
stringBuilder.Append("El campo Descripción es obligatorio");
if (string.IsNullOrEmpty(producto.Marca))
stringBuilder.Append(Environment.NewLine + "El campo Marca es
obligatorio");
if (producto.Precio <= 0)
stringBuilder.Append(Environment.NewLine + "El campo Precio es
obligatorio");
return stringBuilder.Length == 0;
}
}
}
Analice cada uno de los métodos y funciones que creamos en nuestro primer objeto de nuestra
capa de Negocio, observe:
Cada uno de ellos tiene una sola responsabilidad
La capa de negocio cumple otras tareas y no solo la de ser un puente entre nuestra
Capa de Datos y nuestra Capa de Presentación
Como es que estamos usando únicamente objetos y no controles, recuerde que esta
capa tampoco sabe que tipo de proyecto es el que la estará usando.
Observe que esta capa no tiene referencias a System.Configuration ni mucho menos
a System.Data.SqlServerCe.
Observe que en esta capa también se utiliza la Capa de Entidades.
Capa de Presentación o User Interface
Le toca el turno a la Capa de Presentación entrar en escena, esta capa será la encargada de
interactuar con el usuario, la vista de todo nuestro sistema, la encargada de recoger las
peticiones del usuario y de pasar esta misma a la capa de Lógica de Negocio, todo lo que el
usuario requiera se la solicitara a esta y todo lo que Lógica de Negocio devuelva esta se la
mostrar al usuario en forma de datos.
11. En el formulario que tenemos por default llamado “Form1” diseñe una interfaz como la
siguiente:
12. Observe las siguientes líneas de código, genere los eventos involucrados y copie el código
de ejemplo:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
//
//Hacemos las importaciones del espacio de nombres de los dos
proyectos que referenciamos
//observe como esta capa solo referencio a Tienda-LogicNegocio y a
Tienda-Entidades
//observe como no se referencia a la clase de acceso a Datos
using Tienda_LogicaNegocio;
using Tienda_Entidades;
namespace Tienda_Presentacion
{
public partial class Form1 : Form
{
//
//
//Creamos las instancias de la clase Eproducto y ProductoBol
private EProducto _producto;
private readonly ProductoBol _productoBol = new ProductoBol();
public Form1()
{
InitializeComponent();
}
//
//Creamos los métodos generales llenando y leyendo objetos
//
private void Guardar()
{
try
{
if (_producto == null) _producto = new EProducto();
_producto.Id = Convert.ToInt32(txtId.Text);
_producto.Descripcion = txtDescripcion.Text;
_producto.Marca = txtMarca.Text;
_producto.Precio = Convert.ToDecimal(txtPrecio.Text);
_productoBol.Registrar(_producto);
if (_productoBol.stringBuilder.Length != 0)
{
MessageBox.Show(_productoBol.stringBuilder.ToString(), "Para
continuar:");
}
else
{
MessageBox.Show("Producto registrado/actualizado
con éxito");
TraerTodos();
}
}
catch (Exception ex)
{
MessageBox.Show(string.Format("Error: {0}",
ex.Message), "Error inesperado");
}
}
if (productos.Count > 0)
{
dgvDatos.AutoGenerateColumns = false;
dgvDatos.DataSource = productos;
dgvDatos.Columns["columnId"].DataPropertyName = "Id";
dgvDatos.Columns["columnDescripcion"].DataPropertyName
= "Descripcion";
dgvDatos.Columns["columnMarca"].DataPropertyName =
"Marca";
dgvDatos.Columns["columnPrecio"].DataPropertyName =
"Precio";
}
else
MessageBox.Show("No existen producto Registrado");
}
if (_producto != null)
{
txtId.Text = Convert.ToString(_producto.Id);
txtDescripcion.Text = _producto.Descripcion;
txtMarca.Text = _producto.Marca;
txtPrecio.Text =
Convert.ToString(_producto.Precio);
}
else
MessageBox.Show("El Producto solicitado no
existe");
}
catch (Exception ex)
{
MessageBox.Show(string.Format("Error: {0}",
ex.Message), "Error inesperado");
}
}
MessageBox.Show("Producto eliminado
satisfactoriamente");
TraerTodos();
}
catch (Exception ex)
{
MessageBox.Show(string.Format("Error: {0}",
ex.Message), "Error inesperado");
}
}
//
//
//Usamos nuestros metodos y funciones generales, observe como
no hemos repetido codigo en ningun lado
//haciendo con esto que nuestras tareas de actualizacion sean
mas sencillas para nosotros o para
//al asignado en realizarlas...
private void btnAgregar_Click(object sender, EventArgs e)
{
Guardar();
}
TraerPorId(Convert.ToInt32(txtId.Text));
}
}
Guardar();
}
}
De nuevo observe como esta capa desconoce si existe una capa de Datos y mucho menos que
motor de base de datos se utiliza, tampoco se encarga de implementar las reglas de validación
ni de lógica de negocio, su tarea es interactuar con el usuario pidiendo y desplegando
información.
Solo nos resta probar el proyecto…pero eso, será tarea suya…Ejecute el proyecto, inserte un
nuevo registro, busque un registro por id, edite su información, elimine un producto, depure el
código línea a línea para viva de paso a paso como es que va pasando de capa de capa
enviando y trayendo información.
Aquí termina nuestro articulo sobre Arquitectura 3 Capas, espero haya sido de su agrado y que
la explicación haya sido lo bastante clara, en caso de que tenga alguna duda por favor deje su
pregunta en la sección de comentarios o escríbame por medio del formulario de contacto.
Ejemplo C#
Ejemplo Vb.Net
Publicado por José Luis García Bautista en 23:16
Enviar por correo electrónicoEscribe un blogCompartir con TwitterCompartir con
FacebookCompartir en Pinterest
Etiquetas: Arquitectura 3 Capas .Net, Programacion en 3 Capas, Programacion en capas C#