martes, 23 de enero de 2007

“Entidades de Negocio Inteligentes”

En el desarrollo de una arquitectura de n-capas el termino de Entidad de negocio representa el estado de la información que pasará entre cada capa como un conjunto de datos que identifican una entidad como lo es un cliente, una factura, la nomina, la nomenclatura contable, un deposito, etc.

La forma de implementar estas entidades puede variar desde un DataSet hasta un archivo XML que define la estructura de la entidad, en mi experiencia las clases personalizadas para el negocio que no incluyen métodos en su definición sino solamente propiedades que guardan el estado de los datos es una práctica siemple y efectiva.

En este orden de ideas quisiera acuñar este concepto en las mentes de los desarrolladores... Que sucedería si tuviéramos estas clases llamadas Entidades de Negocio pero además de las propiedades que lo definen como lo es un código, un nombre, la dirección, el número del documento o una fecha también pudiéramos definir en cada propiedad el mapeo correspondiente a la columna y al parámetro que requieren para persistir o filtrar la información del proveedor de los datos. Y como si esto fuera suficiente pudiéramos definir la cadena de conexión necesaria para conectarnos al proveedor de datos y definir el nombre de los procedimientos almacenados de los métodos para crear, actualizar, eliminar y listar la información que persiste en el proveedor de datos. Si todo esto es posible gracias a la definición de los atributos dentro del .NET Framework que están disponibles desde la versión 1.1 y gracias a la versión 2.0 podemos utilizar algo conocido como "Generics" para definir tipos Genéricos como clases. A través de un "Parser" y un "Engine" podemos definir la capa de Acceso a Datos única para procesar cualquier entidad del negocio hacia el proveedor de los datos.

El código en C# 2.0 luce de la siguiente manera; Para las entidades de negocio así:

[ Serializable,ConnectionStringName("dbJornalizacion"),CreateMethodName( "TRNTipoTransaccionCrear"),ReadMethodName("TRNTipoTransaccionListar"),UpdateMethodName ("TRNTipoTransaccionActualizar"),DeleteMethodName("TRNTipoTransaccionEliminar")]

public class TipoTransaccion

{

public TipoTransaccion() { }

public TipoTransaccion(int codigo)

{

this.Codigo = codigo;

}

private int? _codigo;

[ColumnName("CodigoTRNTipoTransaccion"), ParameterName("@CodigoTRNTipoTransaccion" ,EsLlavePrimaria=true,IncluirCrear=false)]

public int? Codigo

{

get { return _codigo; }

set { _codigo = value; }

}

private string _nombre;

[ColumnName("Nombre"), ParameterName( "@Nombre")]

public string Nombre

{

get { return _nombre; }

set { _nombre = value; }

}

}

Para la invocación de la capa de Datos como no existe desde la capa lógica de negocio luce así:

public static class TipoTransaccionBLL

{

public static int? Crear(TipoTransaccion parametro)

{

return EngineDAL<TipoTransaccion>.Crear(parametro);

}

public static List<TipoTransaccion> Listar( TipoTransaccion parametro)

{

return EngineDAL<TipoTransaccion>.Listar(parametro);

}

public static bool Actualizar(TipoTransaccion parametro)

{

return EngineDAL<TipoTransaccion>.Actualizar(parametro);

}

public static bool Eliminar(TipoTransaccion parametro)

{

return EngineDAL<TipoTransaccion>.Eliminar(parametro);

}

}

El "Engine" de la Capa de Acceso a Datos luce así, y aunque sea redundar esta "Engine" funciona para cualquier entidad de negocio definida como lo vimos en el primer código. Eso si respetando el estándar de implementación que se definió en la clase EngineDAL, que es para los métodos de crear siempre devuelve un valor entero que es le codigo o el numero, para las operaciones de actualizar y eliminar un valor binario si fue exitosa o no la operación y para le método listar devuelve un colección de entidades o un DataSet.

public class EngineDAL<E> where E:class, new()

{

public static int? Crear(E parametro)

{

int? Id = null;

string connectionStringName = Parser.ObtenerValorConnectionStringAttribute<E>();

string nombreSP = Parser.ObtenerValorMethodNameAttribute<E,CreateMethodNameAttribute >();

Database db = DatabaseFactory.CreateDatabase(connectionStringName);

DbCommand cmd = db.GetStoredProcCommand(nombreSP);

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

object codigo = db.ExecuteScalar(cmd);

if (codigo != null)

{

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

}

return Id;

}

public static bool Actualizar(E parametro)

{

bool OperacionExitosa = false;

string connectionStringName = Parser.ObtenerValorConnectionStringAttribute<E>();

string nombreSP = Parser.ObtenerValorMethodNameAttribute<E,UpdateMethodNameAttribute >();

Database db = DatabaseFactory.CreateDatabase(connectionStringName);

DbCommand cmd = db.GetStoredProcCommand(nombreSP);

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

OperacionExitosa = db.ExecuteNonQuery(cmd)> 0;

return OperacionExitosa;

}

public static bool Eliminar(E parametro)

{

bool OperacionExitosa = false;

string connectionStringName = Parser.ObtenerValorConnectionStringAttribute< E>();

string nombreSP = Parser.ObtenerValorMethodNameAttribute<E,DeleteMethodNameAttribute >();

Database db = DatabaseFactory.CreateDatabase(connectionStringName);

DbCommand cmd = db.GetStoredProcCommand(nombreSP);

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

OperacionExitosa = db.ExecuteNonQuery(cmd) > 0;

return OperacionExitosa;

}

public static List<E> Listar(E parametro)

{

List<E> Listado = null;

string connectionStringName = Parser.ObtenerValorConnectionStringAttribute<E>();

string nombreSP = Parser.ObtenerValorMethodNameAttribute<E,ReadMethodNameAttribute >();

Database db = DatabaseFactory.CreateDatabase(connectionStringName);

DbCommand cmd = db.GetStoredProcCommand(nombreSP);

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

IDataReader idr = null;

try

{

idr = db.ExecuteReader(cmd);

Listado = Parser.Query<E>(idr);

}

finally

{

if (idr != null && !idr.IsClosed) idr.Dispose();

}

return Listado;

}

public static DataSet ListarDS(E parametro)

{

DataSet DS = null;

string connectionStringName = Parser.ObtenerValorConnectionStringAttribute<E>();

string nombreSP = Parser.ObtenerValorMethodNameAttribute<E, ReadMethodNameAttribute >();

Database db = DatabaseFactory.CreateDatabase(connectionStringName);

DbCommand cmd = db.GetStoredProcCommand(nombreSP);

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

DS = db.ExecuteDataSet(cmd);

return DS;

}

public static bool Existe(E parametro)

{

bool Existe = false;

string connectionStringName = Parser.ObtenerValorConnectionStringAttribute<E>();

string nombreSP = Parser.ObtenerValorMethodNameAttribute<E,ExistsMethodNameAttribute >();

Database db = DatabaseFactory.CreateDatabase(connectionStringName);

DbCommand cmd = db.GetStoredProcCommand(nombreSP);

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

object valor = db.ExecuteScalar(cmd);

if (valor != null) Existe = (bool)valor;

return Existe;

}

}

Por cuestiones de abreviar el ejemplo y mostrar la optimización de la capa de acceso a datos en base a las "entidades de negocio inteligentes", se eliminaron las líneas extras, validaciones y procesos que pudieran existir en la capa de negocios.

Hasta el próximo articulo nos vemos!.

Manolo Herrera

5 comentarios:

Kementeus Xtian dijo...

Casualmente el modelo que expresas es un principio de la programación por aspectos de acceso a data, en la que nos podemos concentrar en el fabricar nuestras entidades de negocio y mediante atributos "adornar" nuestras entidades para obtener los datos como sea necesario. Según la documentación de las patterns de Microsoft no es algo muy lejano, de hecho aconsejan desarrollar tal "framework" para proyectos de largo alcance y con un giro de negocio estable (el articulo de MSDN lo puedes encontrar en (http://msdn2.microsoft.com/en-us/library/ms978496.aspx) y de hecho hay un articulo muy bueno de varias partes en CodeProject sobre como implementarlo en la .Net Framework (http://www.codeproject.com/cs/database/dal1.asp).
Creo que ya estamos cerca de llegar a este nivel de desarrollo, especialmente con la venida de metodologias como Software Factories y DSLs. Saludos Manolo!

Genderson dijo...

hola amigo, quiciera saber si me puedes mandar el caso de "Entidades de Negocio Inteligentes" en Visual Basic.net. Estoy muy interesado en implementar esta nueva metodología y haber si tienes otro articulo del tema publicalo por favor. Gracias.

moviedo dijo...

Me gustaria saber si en este ejemplo utilizas la Enterprise Library. Otra cosa es q no aparece el codigo de la clase Parser, si es posible me puedes enviar dicho codigo o el ejmplo completo al correo:

angel_mgl@hotmail.com

Bruno dijo...

Hola, podrias publicar o mandar el codigo de la clase Parser mi mail es: bruno_candia@gmail.com

gracias.

Desarrollo + dijo...

Puedes bajar todo el framework en la siguiente dirección de codeplex:
http://enginedal.codeplex.com/