Beruflich Dokumente
Kultur Dokumente
Hola a todos:
Despus 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 mnimos 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 podra contener errores
gramaticales y sintcticos, el articulo y sus conceptos no pretenden ser la verdad absoluta del
tema, sintase en confianza de dejar sus comentarios y opiniones en la seccin de
comentarios al final del mismo y si lo considera prudente enveme un correo electrnico 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.
Dicho todo lo anterior, comencemos
La separacin de roles en tres capas, hace mas fcil reemplazar o modificar a una, sin
afectar a los mdulos restantes
El cdigo de la capa intermedia puede ser reutilizado por mltiples
Capacidad de migrar nuestro motor de Base de Datos sin grandes impactos al resto del
proyecto.
Hasta aqu, hemos visto la teora de lo que representa la Arquitectura 3 Capas, pero
Como es que debo representar la Arquitectura 3 Capas en un proyecto de Visual
Studio?
Para haya es a donde vamos.
1. Abra el Visual Studio 2012 y cree un proyecto Vaco y nmbrelo CSharp-3Capas-Primer
Entrega
2. Dirjase al Explorador de soluciones y haga click derecho sobre el proyecto que acabamos
de crear:
4. Seleccione, Instalado > Visual C# > Windows > Aplicacin de Windows Forms > En el
campo Nombre escriba, Tienda-Presentacion y presione el botn Aceptar.
Observe que cuando creamos el proyecto principal en automtico se creo un proyecto vaco,
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 vaco), recuerde que el proyecto de Entidades en
este caso Tienda-Entidades no es una capa, mas bien, complementa a la capa de Lgica de
Negocio.
Continuemos con nuestro diseo
Capa de Entidades
Recuerde que en esta capa estarn 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, Descripcin 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.
Para declarar nuestra Entidad Producto:
1. Agregue una clase al proyecto Tienda-Entidades y llmela EProducto (La letra E es por
convecin, 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 lneas
de cdigo:
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 tuviramos mas tablas en nuestra base de datos tendramos que crear la misma cantidad de
objetos en nuestra capa de Entidades y dentro contendran la misma cantidad de propiedades
como campos tiene la tabla siempre respetando el tipo de dato con lo que estos campos estn
declarados.
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.
Volvamos a nuestra Capa de Datos
4. Agreguemos las referencias a las libreras System.Configuration para esto, Click sobre
nuestra capa de datos (proyecto Tienda-AccesoDatos) > Agregar Referencia
y Eliminar registros de la tabla Productos, requiere de una comunicacin directa con la capa de
Entidades, para por ejemplo, al momento de que se le solicite la insercin de un Producto en
lugar de enviarle 5 parmetros 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.
Para lograr la comunicacin entre la Capa de Datos y la Capa de Entidades se requiere de la
referencia de una en la otra, en este caso la referencia de la Capa de Entidades dentro de la
Capa de Datos, para ello:
7. Click derecho sobre nuestro proyecto Tienda-AccesoDatos > Agregar Referencia
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,
adems tiene que declarar la clase como publica (para que la Capa de Lgica de Negocio
pueda tener acceso a ella)
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 Mtodo 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 Garca 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 Accin 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();
}
}
de
la
de
de
/// <summary>
/// Devuelve una lista de Productos ordenados por el campo Id
manera Ascendente
/// </summary>
/// <returns>Lista de productos</returns>
/// <autor>Jos Luis Garca Bautista</autor>
public List<EProducto> GetAll()
{
//Declaramos una lista del objeto EProducto la cual ser
encargada de
//regresar una coleccin de los elementos que se obtengan
la BD
//
//La lista substituye a DataTable utilizado en el proyecto
ejemplo
List<EProducto> productos = new List<EProducto>();
}
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 Garca 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 Garca Bautista</autor>
public void Delete(int idproducto)
{
using (SqlCeConnection cnx = new
SqlCeConnection(ConfigurationManager.ConnectionStrings["cnnString"].To
String()))
{
cnx.Open();
9. Agregue una nueva clase y llmela ProductoBol, haga los using de las referencias que
acabamos de crear, establezca el nivel de acceso como public y copie la siguiente estructura
de cdigo:
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 tambin 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 mtodo para Insertar un nuevo Producto,
observe como este mtodo tampoco valida los el contenido
//de las propiedades, sino que manda a llamar a una Funcin
que tiene como tarea nica hacer esta validacin
//
public void Registrar(EProducto producto)
{
if(ValidarProducto(producto))
{
if (_productoDal.GetByid(producto.Id) == null)
{
_productoDal.Insert(producto);
}
else
_productoDal.Update(producto);
}
}
public List<EProducto> Todos()
{
return _productoDal.GetAll();
}
public EProducto TraerPorId(int idProduct)
{
stringBuilder.Clear();
if (idProduct == 0) stringBuilder.Append("Por favor
proporcione un valor de Id valido");
if(stringBuilder.Length == 0)
{
return _productoDal.GetByid(idProduct);
}
return null;
}
public void Eliminar(int idProduct)
{
stringBuilder.Clear();
if (idProduct == 0) stringBuilder.Append("Por favor
proporcione un valor de Id valido");
if (stringBuilder.Length == 0)
{
_productoDal.Delete(idProduct);
}
}
private bool ValidarProducto(EProducto producto)
{
stringBuilder.Clear();
if (string.IsNullOrEmpty(producto.Descripcion))
stringBuilder.Append("El campo Descripcin 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 mtodos 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 Presentacin
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.
11. En el formulario que tenemos por default llamado Form1 disee una interfaz como la
siguiente:
12. Observe las siguientes lneas de cdigo, genere los eventos involucrados y copie el cdigo
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 mtodos 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");
}
}
private void TraerTodos()
{
List<EProducto> productos = _productoBol.Todos();
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");
}
private void TraerPorId(int id)
{
try
{
_producto = _productoBol.TraerPorId(id);
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");
}
}
private void Eliminar(int id)
{
try
{
_productoBol.Eliminar(id);
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();
}
private void txtId_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Enter && !
string.IsNullOrWhiteSpace(txtId.Text))
{
e.SuppressKeyPress = true;
TraerPorId(Convert.ToInt32(txtId.Text));
}
}
private void txtPrecio_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Enter)
{
e.SuppressKeyPress = true;
Guardar();
}
}
private void btbnBuscar_Click(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(txtId.Text))
{
TraerPorId(Convert.ToInt32(txtId.Text));
}
}
private void btnEliminar_Click(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(txtId.Text))
{
Eliminar(Convert.ToInt32(txtId.Text));
}
}
}
}
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 validacin
ni de lgica de negocio, su tarea es interactuar con el usuario pidiendo y desplegando
informacin.
Que es lo que falta?
Solo nos resta probar el proyectopero eso, ser tarea suyaEjecute el proyecto, inserte un
nuevo registro, busque un registro por id, edite su informacin, elimine un producto, depure el
cdigo lnea a lnea para viva de paso a paso como es que va pasando de capa de capa
enviando y trayendo informacin.
Aqu termina nuestro articulo sobre Arquitectura 3 Capas, espero haya sido de su agrado y que
la explicacin haya sido lo bastante clara, en caso de que tenga alguna duda por favor deje su
pregunta en la seccin de comentarios o escrbame por medio del formulario de contacto.
Escribir este articulo me llevo mas de 4 horas, dejar un comentario de agradecimiento le tomara
5 minutos.