sábado, 6 de enero de 2007

Programación en Capas Primera Parte (Capa de Acceso a Datos)

Esta metodología de trabajo a probado ser muy efectiva durante ya varias décadas. Cada vez mas las herramientas de desarrollo como .NET nos permiten acercarnos mas y mas a los principios de programación OOP. La separación de programación en capas para aplicaciones comerciales permite la reutilización, el polimorfismo, la independencia y cooperación entre objetos o componentes. Pero sucede que cuando iniciamos la implementación de esta metodología nos es difícil aplicar los principios de abstracción y encapsulación. Para ello es este tema que espero desarrollar en este y otros artículos y juntos podamos aprender. Nivel Requerido para lectores: Medio.

El modelo de N-Capas conocido en ingles como N-tiers o Layers define las siguientes capas de Abajo hacia arriba:

Capa de acceso a datos: Capa que sirve entre como puente entre la capa lógica de negocio y el proveedor de datos. Este capa pretende encapsular las especificidades del proveedor de datos tales como (SQL, Oracle, Sybase, archivos XML, texto, hojas electrónicas), a la siguiente capa. Para que si cambia el proveedor de datos solo necesitemos cambiar en una sola capa el proveedor de datos. Hoy en día gracias a la tecnología disponible y a la expansión del conocimiento a través del Internet, tenemos a nuestra deposición la librería de Microsoft Enterprise Library en su versión Dos, donde podemos acceder sin necesidad de cambiar el código a proveedores OLEDB, SQL, Oracle, XML, archivos Excel, etc. Por lo que si programamos en .Net por capas la capa de acceso a datos debemos de utilizar estas librerías para dejarle el trabajo a la misma y nosotros solo preocuparnos con la conexión al proveedor de los datos y nada mas. Un ejemplo de cómo luce esta capa aplicando las librerías de Microsoft es como muestro a continuación esta escrito en C# 2.0:

using System;

using System.Collections.Generic;

using System.Text;

using System.Data;

using Microsoft.Practices.EnterpriseLibrary.Common;

using Microsoft.Practices.EnterpriseLibrary.Data;

using System.Data.Common;

namespace LaUnion.Sistema

{

public static class ConceptoJornalizacionDAL

{

private const string NombreInstancia = "dbJornalizacion";

private const string SPCrear = "TRNConceptoJornalizacionCrear";

private const string SPActualizar = "TRNConceptoJornalizacionActualizar";

private const string SPEliminar = "TRNConceptoJornalizacionEliminar";

private const string SPListar = "TRNConceptoJornalizacionListar";

public static int? Crear(ConceptoJornalizacion parametro)

{

int? Resultado = null;

Database db = DatabaseFactory.CreateDatabase(NombreInstancia);

DbCommand cmd = db.GetStoredProcCommand(SPCrear);

Parser.ParametrosSoloIncluirCrear<ConceptoJornalizacion>(ref db, ref cmd, parametro);

object codigo = db.ExecuteScalar(cmd);

if (codigo != null)

{

int valor = 0; int.TryParse(codigo.ToString(),out valor); Resultado = valor;

}

return Resultado;

}

public static bool Actualizar(ConceptoJornalizacion parametro)

{

bool Resultado = false;

Database db = DatabaseFactory.CreateDatabase(NombreInstancia);

DbCommand cmd = db.GetStoredProcCommand(SPActualizar);

Parser.ParametrosSoloActualizable<ConceptoJornalizacion>(ref db, ref cmd, parametro);

db.ExecuteNonQuery(cmd);

Resultado = true;

return Resultado;

}

public static bool Eliminar(ConceptoJornalizacion parametro)

{

bool Resultado = false;

Database db = DatabaseFactory.CreateDatabase(NombreInstancia);

DbCommand cmd = db.GetStoredProcCommand(SPEliminar);

Parser.ParametrosSoloLlavePrimaria<ConceptoJornalizacion>(ref db, ref cmd, parametro);

db.ExecuteNonQuery(cmd);

Resultado = true;

return Resultado;

}

public static List<ConceptoJornalizacion> Listar(ConceptoJornalizacion parametro)

{

Database db = DatabaseFactory.CreateDatabase(NombreInstancia);

List<ConceptoJornalizacion> resultado = null;

DbCommand cmd = db.GetStoredProcCommand(SPListar);

Parser.ParametrosSoloFiltro<ConceptoJornalizacion>(ref db, ref cmd, parametro);

IDataReader idr = db.ExecuteReader(cmd);

resultado = Parser.Query<ConceptoJornalizacion>(idr);

idr.Close();

return resultado;

}

}

}

Importante de notar en este código es que solo hay métodos y no propiedades y estos son estáticos, ya que la capa de datos no necesita guardar el estado de la clase sino solo transformar la información que viene del proveedor de los datos a una colección de entidades de negocio. Lo que se recibe en cada método es una Entidad de negocio que tiene propiedades como código, nombre, valor, dirección, etc.

Los métodos invocan store procederes en la bases de datos, o comandos de Transq-Sql estándar para los proveedores que no tengan store procederes deben de cumplir operaciones simples en la base de datos ya que la capa lógica de negocios debe de contener la complejidad del negocio, el flujo del proceso del negocio, las validaciones pertinentes de los datos que van hacer almacenados en el proveedor de los datos através de un puente, interfaz o capa la de Acceso a los Datos. Los métodos deben de reducirse a operaciones elementales como crear registros, actualizarlos, eliminarlos o mostrarlos. Cualquier derivado de estos puede estar en la siguiente capa en la del negocio. Un método admisible en esta capa puede ser el Existe que verifica únicamente si el registro en el proveedor de datos existe o no, y otros métodos muy particulares que cumplan tareas muy especificas como cálculos. Normalmente estas operaciones son denominadas CRUD de sus siglas en ingles (Create, Read, Update, Delete). Entre mas simples sean las operaciones que el proveedor de los datos realice estaremos trasladando a la capa de la lógica del negocio dicha complejidad, con ello garantizamos que en un solo lugar definimos esta lógica de flujo y reglas y no en la que me sirve únicamente para comunicarme con el proveedor de los datos. Esto trae otro beneficio intrínseco y es que no necesitamos programadores que tengan mucho conocimiento en base de datos sino en el lenguaje que desarrollando porque trasladamos la complejidad y lógica de negocio a la capa superior.

Los parámetros deben de poderse filtrar según sea el caso a través de la nulidad que nos proveed la versión 2.0 del .Net Framework, para que una operación fundamental de actualización pueda servir para cambiar el estado a un documento de ingresado a emitido o de emitido a anulado, utilizamos el mismo método para realizar distintas operaciones, si es necesario actualizar otras entidades o procesos del negocio la capa superior se encargará de ello.

Algo importante que aclarar es que estas reglas no se aplican para reportes o consultas que necesariamente necesitan aprovechar el motor del proveedor de los datos, sino para operaciones de mantenimiento y procesos del negocio.

En resumen en la capa de datos podemos decir:

1) No guarda el estado (stateless)

2) Sus parámetros deben de ser Entidades de Negocio (Clases que si tiene el valor de cada propiedad, ejemplo: Cliente, Factura, nomina, etc.).

3) Los métodos mas comunes se denominan CRUD (Crear, Leer, Actualizar o Eliminar) y realizan operación elementales en el proveedor de los datos.

4) No debe de existir ninguna validación ni operaciones complejas en esta capa solo las pertinentes para operar con el proveedor de los datos, como es el manejo de los nulos en los parámetros y en los datos que se reciben del proveedor.

Recomendaciones: Utilizar los estándares de nombramiento CAMEL, PASCAL y la forma de operar de cada método se busque un patrón estándar, por ejemplo que el crear pueda devolver el número o código creado y que se haga a través de un parámetro output o un SELECT @@IDENTITY después de la inserción del registro en el procedimiento almacenado del proveedor de los datos. Una excelente guía para el nombramiento y utilización de mayúsculas y minúsculas puedes encontrarla en el help de Visual Studio 2005:

ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.NETDEVFX.v20.en/dv_fxdesignguide/html/5fbcaf4f-ea2a-4d20-b0d6-e61dee202b4b.htm

Espero haber despertado el interés por la programación en capas y les sea útil en sus labores.

Hasta pronto,

Manolo Herrera

Pd. La Clase Parser, no es parte de los estandares de Microsoft sino que son propios que utilizo para pasar los valores de las entidades a los parámetros o los valores del proveedor de los datos a las entidades. En otro articulo sobre Entidad de Negocios y Parser mostraré el código y escribiré al respecto.

Recomiendo vean los otros árticulos bajo diseño y arquitectura de mi blog. Los interesados en ver código sobre lo que he escrito aunque algo viejo les dejo la dirección a continuación: http://enginedal.codeplex.com/

5 comentarios:

Just Dance! dijo...

¿Como logras que no se produzca la referencia circular entre proyectos si uno de ellos es la capa de negocio y el otro proyecto es la capa de acceso a datos y ambos se tienen que ver uno al otro?

César Eduardo Suárez dijo...

Interesante el post, en este momento estoy algo aturdido con tanta inforamcion y la verdad veo muy parecido lo relacionado a mvc y layers, no se me puedes dar una explicacion ams clara.

Felicidades

blog.waspgroup.com
csuarez@waspgroup.com

Unknown dijo...

Hola, quise tratar de aplicar tu ejemplo en el cual usas la libreria de Microsoft y no pude ya que esta libreria me da error, el problema es el siguiente:

estoy haciendo una aplicacion multicapa en la cual me voy a conectar a una base de datos sql server,usando la libreria de Microsoft, Enterprise Library, entonces baje e instale la version Microsoft Enterprise Library 4.1 - October 2008. El problema que tengo es que cuando voy a agregar la referencia Microsoft.Practices.EnterpriseLibrary.Common; y
Microsoft.Practices.EnterpriseLibrary.Data; haciendo References --> Click derecho --> Agregar referencia, estas no me aparecen, me aparecen otras que se instalaron cuando ejecute el programa
Microsoft Enterprise Library 4.1 - October 2008.

Entonces ejecuto el script BuildLibrary.bat y me arroja los errores:


ConnectionStringEditor.cs<18,17>: error CS0234: El tipo o el nombre del espacio de nombres 'Data' no existe en el espacio de nombres 'Microsoft'<¿falta una referencia de ensamblado?>

ConnectionStringEditor.cs<105,8>: error CS0246: No se puede encontrar el tipo o el nombre de espacio de nombres 'DataSource' <¿falta una directiva using o una referencia de ensamblado?>

ConnectionStringEditor.cs<106,17>: error CS0246: No se puede encontrar el tipo o el nombre de espacio de nombres 'DataProvider' <¿falta una directiva using o una referencia de ensamblado?>

revise el archivo en cuestion, la llamada a Microsoft.Data y todo parece estar bien, he probado de todo y nada me funciona, por cualquier idea gracias.

Kiquenet dijo...

Señor, se puede descargar el código completo ? Gracias.

Juan Manuel Herrera dijo...

Estimado kiquenet, he dejado la referencia al final del artículo para que bajes el código. Para que lo revises.