martes, 20 de febrero de 2007

Capa Lógica de Negocios

Aquí les comparto este articulo que les comparti a los amigos de MSCoder.
Introducción

Hoy en día todos los desarrolladores comprendimos que es necesaria una capa intermedia entre nuestra aplicación y el proveedor de los datos, conocida como "Capa de Acceso a los Datos", pero muy pocos han comprendido las implicaciones de continuar desarrollando aplicaciones que contengan "súper botones" que en la capa de presentación tiene toda la lógica del negocio. El problema de este enfoque es que estaremos desarrollando aplicaciones para el día, día sin una planificación que nos permita construir una infraestructura de software que nos permita disponer de servicios que puedan ser reutilizados y extendidos por las aplicaciones existentes o nuevas. Otro problema de no desarrollar aplicaciones con arquitectura de n-capas es que se abre una brecha o GAP de seguridad cada vez que necesitamos reescribir el código para otra plataforma sin garantizar que la "misma" aplicación se comporte igual sin importar la plataforma o dispositivo en que se desarrolla la nueva capa de presentación de nuestra aplicación.

Este articulo ha sido escrito asumiendo que ya se ha tenido alguna experiencia desarrollando aplicaciones con C# , ya que no explica el uso del lenguaje sino la aplicación de la arquitectura n-capas en aplicaciones desarrollados en la plataforma .NET Framework 2.0.

Definición

El tener una arquitectura n-capas bien definida reducirá los hoyos de vulnerabilidad que evidentemente se dan cuando no desarrollamos con esta arquitectura. Dentro de esta arquitectura la capa intermedia o la capa lógica de negocios ocupa un lugar angular en la construcción de una infraestructura de software que nos permitirá el crecimiento y la extensibilidad de servicios para todas las aplicaciones existentes y futuras.

La definición de los linderos de cada capa nos permitirá definir correctamente la colaboración que proveerá cada una de ellas y descubriremos que la capa intermedia es inevitablemente la lógica de negocios, esto hará una infraestructura robusta y lista para la extensibilidad y el crecimiento como proveedora de servicios. Veámoslo más a detalle:

La capa de acceso a los datos su propósito primario es separar al proveedor de datos del resto de la aplicación. Hoy en día gracias a la tecnología que nos provee el .NET Framework esta capa puede ser reutilizada para diversos proveedores sin hacer cambios significativos o prácticamente sin ningún cambio. Pero esta capa no debe de guardar ningún grado de complejidad mas que el ejecutar las operaciones fundamentales del proveedor de datos operaciones conocidas por sus siglas en inglés CRUD (Creación, Lectura, Actualización y Eliminación). Las Métodos de esta capa debe de realizar operaciones mono transaccionales, o atómicas. Ya que la capa superior, la intermedia se encargará de manejar las transacciones gracias a la disponibilidad del System.Transactions del .NET Framework 2.0 de la clase TransactionScope. He aquí la colaboración entre las capas, la capa de acceso a datos se convierte en una capa un sencilla gracias a que la capa intermedia se encargará de las transacciones y esto gracias a la tecnología disponible que tenemos en el .NET Framework 2.0.

A continuación mostraré un segmento de código que muestre lo expuesto.

public static class ConceptoTransaccionBLL

{

private const decimal FactorCalculoImpuesto = 0.10141728;

public const string CrearExplicacion = "Proceso bla, bla bla";

public static int? Crear(ConceptoTransaccion parametro)

{

int? Resultado = null;

if (ValidarCrear(parametro)

{

using (TransactionScope ts = new TransactionScope())

{

Resultado = ConceptoTransaccionDAL.Crear(parametro);

parametro.Nombre = "Prueba de Transaccion";

parametro.ValorImpuesto = parametro.Valor * FactorCalculoImpuesto;

OtraTransaccionDAL.Actualizar(parametro);

ts.Complete();

}

}

return Resultado;

}

}

Este segmento de código típico de la capa intermedia o de negocios nos muestra lo siguiente:

El flujo del proceso del negocio:

Como el ejemplo anterior que define que luego de crear El Concepto de la Transacción, Actualizará Otra Transacción.

Resultado = ConceptoTransaccionDAL.Crear(parametro);

OtraTransaccionDAL.Actualizar(parametro);

Las reglas del negocio:

En nuestro segmento de código en medio del proceso realizamos el calculo de un impuesto X.

private const decimal FactorCalculoImpuesto = 0.10141728;

….

parametro.ValorImpuesto = parametro.Valor * FactorCalculoImpuesto;

Para facilitar el ejemplo se declaró como una constante este factor pero bien pudo estar almacenada esta información en el proveedor de datos u en otro medio.

Validaciones de los datos:

Esta capa debe de garantizar que los datos requeridos para procesarla fueron debidamente validados en el ejemplo anterior encapsulamos estas validaciones en el método validar, y solo si es exitosa la validación puede iniciar el flujo del proceso del negocio.

if (ValidarCrear(parametro){{…}}

Textos de explicación de los procesos del negocio:

Estos textos aunque pertenecen a esta capa pueden ser útiles en la capa de presentación para informar al usuario de que se trata el proceso que ha seleccionado ejecutar.

public const string CrearExplicacion = "Proceso bla, bla bla";

Manejo de transacciones:

En esta capa utilizamos la librería de .NET Framework 2.0, System.Transactions para manejar las transacciones que se adaptan muy bien si en la capa de acceso a los datos estoy utilizando Las librerías de Práticas y Patrones " Microsoft Enterprise Library 2.0 de enero del 2006" que utiliza las mejores prácticas de acceso a los datos de la tecnología ADO.NET 2.0. La clase TransactionScope hace todo el trabajo por nosotros. Además podemos definir Opciones para variar el comportamiento del alcance de las transacciones, lo cual por simplificación no expondremos aquí pero lo invito a que investigue en la Red.

using (TransactionScope ts = new TransactionScope())

{

ts.Complete();

}

Ts.Complete() si llega ejecutarse realiza el commit o persistencia "física" en el proveedor de los datos, si se interrumpe el código antes de ejecutar esta sentencia, entonces hará un rollback(revertira lo actualizado en el proveedor de datos) de las transacciones que hasta ese momento se ejecutaron. Esta fuera del alcance de este documento enseñar a los lectores el manejo de transacciones, pero si es mi objetivo dejar una inquietud de conocer a detalle el manejo sencillo y eficiente de las transacciones en el .NET Framework 2.0.

Facilita su lectura y manejo en la Capa de Presentación

Por lo tanto ya que hemos encapsulado la lógica en la capa intermedia, en la capa de presentación solo tendremos que llenar la clase que representa la entidad de negocio e invocar el método de la capa de negocios para procesar la información como se muestra en el siguiente segmento de código:

private void Grabar()

{

try

{

Factura parametro = this.Factura;

parametro.UsuarioSistema = this.User.Identity.Name;

TransaccionBLL.Crear(parametro);

MessageBox.Show("Operacion realiza con Exito!");

}

catch (Exception ex)

{

MessageBox.ShowError(ex.Message);

}

}

En este segmento invocamos un Método en la capa de presentación Grabar que a su vez, invoca de la capa de negocio TransaccionBLL y su método Crear que recibirá como único parámetro la entidad Factura. Y eso es todo.

La entidad del negocio

Es muy importante definir la estratégica de comunicación entre las capas. En el ejemplo expuesto hemos utilizado Entidades de Negocio, representadas por Clientes, Facturas, Empleados, Nomina Quincenal, Movimientos Bancarios, etc.

Estas entidades carecen de métodos solo tiene propiedades, campos y posiblemente atributos, que nos pueden servir para almacenar la información como el mapeo de las propiedades a las columnas de la base de datos. Luciría de la siguiente manera:

[ ConnectionStringName("NORTHWIND"), ReadMethodName("OrderRead")]

public class Orden

{

private int? numeroOrden;

[ColumnName("OrderID"), ParameterName( "@OrderID",IsPrimaryKey=true,IsForCreate=false)]

public int? NumeroOrden

{

get { return numeroOrden; }

set { numeroOrden = value; }

}

private DateTime fecha;

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

public DateTime Fecha

{

get { return fecha; }

set { fecha = value; }

}

}

Donde ConnectionStringName, ReadMethodName, ColumnName, ParameterNamel, son atributos personalizados que el programador puede definir y que el .NET Framework utiliza ampliamente desde su versión 1.0. Se recuerda del [Seriealizable] este es un atributo que indica al CLR que la clase puede persistir en algún medio utilizado como lo es una variable de sessión, o una variable ViewState en ASP.NET.

Ahora veamos un ejemplo paso a paso de transformación de un súper botón a una implementación de arquitectura de n-capas.

A continuación les mostrare un ejemplo de un segmento de código que representa el evento de un botón que tiene como propósito insertar una orden de un cliente en la base de datos "Northwind" (incluida en SQL Server 2000). El código lo iremos transformando hasta alcanzar la infraestructura de negocios aplicando los conceptos de arquitectura de n-capas en .NET Framework 2.0.

private void btnInsertar_Click(object sender, EventArgs e)

{ // Validaciones de los datos capturados

if (parametroFechaOrden == null parametroFechaOrden == DateTime.MinValue)

{

MessageBox.Show("Debe de ingresar el numero de la Orden" );

return;

}

if (paremetroCodigoCliente == null paremetroCodigoCliente.Length == 0)

{

MessageBox.Show("Seleccione el cliente");

return;

}

// Segmento de Codigo usual en el manejo de la Capa de Presentacion

this.dataGridView1.Enabled = false; this.btnConsultar.Enabled = false;

// Manejo del acceso a los datos

Database db = DatabaseFactory .CreateDatabase("NORTHWIND");

string sqlCommand = "OrderCreate";

DbCommand orderCommand = db.GetStoredProcCommand(sqlCommand);

db.AddInParameter(orderCommand, "@CustomerID", DbType.String, paremetroCodigoCliente);

db.AddInParameter(orderCommand, "@EmployeeID", DbType.Int32, parametroCodigoEmpleado);

db.AddInParameter(orderCommand, "@OrderDate", DbType.Date, parametroFechaOrden);

sqlCommand = "OrderDetailCreate";

DbCommand orderDetailCommand = db.GetStoredProcCommand(sqlCommand);

using (DbConnection connection = db.CreateConnection())

{

connection.Open();

//manejo de transacciones

DbTransaction transaction = connection.BeginTransaction();

try

{

object valor = db.ExecuteScalar(orderCommand, transaction);

int numeroOrden = 0;

if (!int.TryParse(valor.ToString(), out numeroOrden)) throw new Exception("No pudo obtener el numero de Orden");

foreach (OrdenLinea linea in ordenDetalle)

{

if (orderDetailCommand.Parameters.Count > 0)

orderDetailCommand.Parameters.Clear();

db.AddInParameter(orderDetailCommand, "@OrderID", DbType.Int32, numeroOrden);

db.AddInParameter(orderDetailCommand, "@ProductID", DbType.String, linea.CodigoProducto );

db.AddInParameter(orderDetailCommand, "@UnitPrice", DbType.Decimal, linea.PrecioUnitario );

db.AddInParameter(orderDetailCommand, "@Quantity", DbType.Int32, linea.Cantidad );

db.AddInParameter(orderDetailCommand, "@Discount", DbType.Decimal, linea.Descuento);

db.ExecuteNonQuery(orderDetailCommand, transaction);

}

transaction.Commit();

}

catch (Exception ex)

{

transaction.Rollback();

MessageBox.Show(ex.Message);

this.dataGridView1.Enabled = true; this.btnConsultar.Enabled = true;

return;

}

finally

{

connection.Close();

}

this.dataGridView1.Enabled = true; this.btnConsultar.Enabled = true;

MessageBox.Show( "Ordern Guardada!");

}

}

El código anterior muestra un código típico cuando "no" se utiliza la arquitectura de n-capas, en el desarrollo de aplicaciones empresariales. Es muy difícil interpretar este código porque incluye el manejo de la capa de presentación las validaciones de los datos, el manejo de errores. Adicional a ello si necesitamos realizar esto en otra opción en nuestra aplicación estaremos obligados a repetir el código, y entonces la brecha (GAP) se expande en ves de reducirse en el objetivo de minimizar las entradas maliciosas a nuestra aplicación. Este es otro aspecto el de Seguridad es el que nos obliga a tener una infraestructura de software bien definida para reducir los puntos de vulnerabilidad de nuestra aplicación.

Ahora veamos como quedaría nuestra aplicación si refactorizamos (la disciplina que promueve la mejora del código interno sin afectar el comportamiento externo de una aplicación. Uno de sus expositores mas famosos es Martin Fowler y su libro "Refactoring") este código e implementamos la arquitectura de n-capas:

private void btnInsertar_Click(object sender, EventArgs e)

{

// Segmento de Codigo usual en el manejo de la Capa de Presentacion

this.dataGridView1.Enabled = false; this.btnConsultar.Enabled = false;

Orden orden = new Orden();

orden.CodigoCliente = paremetroCodigoCliente;

orden.Fecha = parametroFechaOrden;

try

{

bool OperacionExitosa = OrdenBLL.Crear(orden, ordenDetalle);

if (OperacionExitosa)

MessageBox.Show("Ordern Guardada!");

}

catch (Exception ex)

{

MessageBox.Show(ex.Message);

}

this.dataGridView1.Enabled = true; this .btnConsultar.Enabled = true;

}

Podemos notar que ahora es mucho mas fácil entender el código para hemos encapsulado en la capa intermedia la de negocios todo el código referente a la lógica del negocio. Deducimos por simple observación que hay una clase "OrdenBLL" que tiene un Método Crear; que Crea la orden (el encabezado y detalle de la orden), alrededor esta el código propio de la capa de presentación y el manejo del error, que para ejemplo solo lo capturamos para evitar una ruptura en el funcionamiento de la aplicación y mostramos el error.

Ahora vayamos mas adentro del código y veamos que encierra el método Crear de la clase "OrdenBLL":

public class OrdenBLL

{

public static bool Crear(Orden orden, OrdenLinea[] ordenDetalle){..}

public static bool Validar(Orden orden){…}

}

Esta clase de la capa intermedia tiene dos métodos uno que crea la orden y otro que valida los valores de la entidad de negocio Orden. Veamos ahora el método crear:

public static bool Crear(Orden orden, OrdenLinea[] ordenDetalle)

{

bool OperacionExistosa = false;

bool CreoDetalle = false;

if (Validar(orden))

{

using (TransactionScope ts = new TransactionScope ())

{

int? numeroOrden = OrdenDAL.Crear(orden);

if (numeroOrden == null numeroOrden == 0)

throw new Exception("No pudo obtener el numero de Orden" );

foreach (OrdenLinea linea in ordenDetalle)

{

linea.NumeroOrden = numeroOrden;

CreoDetalle = OrdenDAL.CrearDetalle(linea);

if (CreoDetalle) break ;

}

if (CreoDetalle)

ts.Complete();

}

}

return OperacionExistosa;

}

Este método se encarga de invocar el método validar que es publico por si se desea validar los datos sin efectuar ninguna operación. Luego de validar los datos esta capa se encarga de manejar las transacciones ya que invocamos dos diferentes métodos de la capa de acceso a datos Y cada uno de ellos maneja su propia conexión. Pero el código es fácilmente entendible indicándonos que hay una clase "OrdenDAL" que tiene un método para crear la orden (el encabezado) y otro el detalle si sucede algo sabemos que esta manejando transacciones y revertirá todo el proceso.

Ahora veamos los métodos de la capa de acceso a datos. Iniciemos con el método que Crea el encabezado de la orden:

public class OrdenDAL

{

public static int? Crear(Orden orden)

{

int? NuevoNumeroOrden = null;

Database db = DatabaseFactory .CreateDatabase("NORTHWIND");

string sqlCommand = "OrderCreate";

DbCommand orderCommand = db.GetStoredProcCommand(sqlCommand);

db.AddInParameter(orderCommand, "@CustomerID", DbType.String, orden.CodigoCliente);

db.AddInParameter(orderCommand, "@EmployeeID", DbType.Int32, orden.CodigoEmpleado);

db.AddInParameter(orderCommand, "@OrderDate", DbType.Date, orden.Fecha);

DbCommand orderDetailCommand = db.GetStoredProcCommand(sqlCommand);

using (DbConnection connection = db.CreateConnection())

{

connection.Open();

//manejo de transacciones

try

{

object valor = db.ExecuteScalar(orderCommand);

int numeroOrden = 0;

int.TryParse(valor.ToString(), out numeroOrden);

}

finally

{

connection.Close();

}

}

return NuevoNumeroOrden;

}

Esto método solo se encarga de pasar los valores de la entidad y ejecutar el procedimiento almacenado. A continuación el método que crea el detalle de la orden:

public static bool CrearDetalle(OrdenLinea detalle)

{

bool operacionExitosa = false;

Database db = DatabaseFactory.CreateDatabase("NORTHWIND" );

string sqlCommand = "OrderDetailCreate";

DbCommand orderDetailCommand = db.GetStoredProcCommand(sqlCommand);

db.AddInParameter(orderDetailCommand, "@OrderID", DbType.Int32, detalle.NumeroOrden);

db.AddInParameter(orderDetailCommand, "@ProductID", DbType.String, detalle.CodigoProducto );

db.AddInParameter(orderDetailCommand, "@UnitPrice", DbType.Decimal, detalle.PrecioUnitario );

db.AddInParameter(orderDetailCommand, "@Quantity", DbType.Int32, detalle.Cantidad);

db.AddInParameter(orderDetailCommand, "@Discount", DbType.Decimal, detalle.Descuento);

int afectados = db.ExecuteNonQuery(orderDetailCommand);

operacionExitosa = afectados > 0;

return operacionExitosa;

}

}

Este método la única diferencia del anterior es que crea el detalle de la orden pero hace lo mismo pasa los valores de la entidad y ejecuta el procedimiento almacenado. Esta capa gracias a la abstracción de la capa intermedia se convierte en una capa que realiza operaciones atómicas, mono-transaccionales, esto facilita su comprensión y reduce el riesgo de un error en el código porque solo hace una casa pasar los datos de la entidad al proveedor de datos. Si hay una falla del lado del proveedor de datos sabemos que es en esta capa o en el mismo proveedor de datos y no disperso por toda la aplicación o la capa de presentación.

El Futuro

Por ultimo quisiera mencionar que hay una fuerte tendencia y mala concepción que la lógica de negocio es más fácil y práctica dejarla en el proveedor de los datos, pero no es así. Si analizamos un poco mas encontraremos que si tenemos un lenguaje orientado a objetos como lo es C#, con disposición a definir Tipos Genéricos, la capacidad de utilizar la reflexión en las entidades, la definición de atributos, podremos reutilizar el código, cambiar su comportamiento, heredar los objetos y abstraer y encapsular el código de una forma mucho mas fácil y eficiente que desde un proveedor de datos relacional. En especial si vemos que en el futuro podremos invocar y manejar las colecciones como lo haríamos desde un proveedor de datos de una forma mas natural o través de funciones extensibles como lo serán el FROM, WHERE y SELECT dentro del mismo lenguaje de programación porque el .NET Framework 3.0 se encargará de generar el código al proveedor de datos o no ha oído o leído sobre LINQ. Aquí le dejo un segmento de código para que se emocione e investigue más al respecto:

from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n }

Código extraído de las especificaciones del Nuevo C# 3.0 Mayo 2006.

Resumen

En resumen si tenemos definida la capa intermedia y su colaboración con la capa de acceso a los datos hacia abajo y hacia arriba la capa de presentación. Si fuera necesario que lo es hoy en día proveer a los usuarios de nuestra aplicación de acceso a otra plataforma o dispositivo de Hardware, solo nos preocuparíamos por crear una nueva capa de presentación y a lo sumo actualizar o modificar la capa de acceso a datos para acceder un proveedor mas liviano para implementar nuestra aplicación en un dispositivo móvil. Pero ya no nos preocuparíamos del negocio en sí, sino de la parte tecnológica que es la parte donde somos más competentes. Esto al largo plazo nos dará mayor seguridad y preparación para enfrentar los retos presentes y futuros, porque hemos desarrollado una infraestructura de software con servicios bien definidos gracias a la implementación de la arquitectura de n-capas.

Hasta una próxima vez!

Manolo Herrera

Líder de la Comunidad de Desarrolladores .NET en Guatemala

17 comentarios:

Unknown dijo...

Excelente Articulo !!!

Unknown dijo...

Y donde estan la transacciones en la DAL para los metodos "Crear" y "CrearDetalle" ???

Ronald_00 dijo...

Una consulta, la validacion de la entidad Orden, como por ejemplo que el campo de la fecha o el numero de cliente no sean nulos, no sería más rápido y práctico hacerlo en la capa de presentación?, como lo expusistes en el superboton.

Juan Manuel Herrera dijo...

Para Ronald
Si es mas practico y debe de hacerse allí, pero que sucede si debes de hacer la misma aplicación para un dispositivo móvil o una aplicación Windows deberás de repetir la validación en la capa de presentación y si se te olvida hacer y no envías el valor permitirá guardarla o marcará un error no manejado en cambio si esto esta en la capa lógica del negocio y cambias la capa de presentación la validación siempre se va hacer la hagas o no en la capa de presentación.

Espero haberte aclarado el punto.

Saludos,

Manolo Herrera

Ronald_00 dijo...

Si, gracias, no lo había analizado desde ese punto de vista, y dime no habria una mejor forma de grabar el Detalle de la Orden, como enviar todo el array a la BD y se evite el acceso continuo a la BD por cada linea de detalle ???. Saludos

Fernando Segura dijo...

De casualidad tenes el código y la bd por allí de este ejemplo Manolo, porque la verdad es muy bueno que des este tipo de artículos.

ShamRagnar dijo...

Excelente articulo. Simple y claro

De aqui y de allá dijo...

Gracias por el artículo, me ha venido muy bien. Tengo una duda sobre las aplicaciones por capas que puede que me puedas resolver. Quiero diseñar una aplicación en capas que lea en una base de datos los valores con el campo Activo = true solo, y los muestre al usuario. En cambio si el usuario modifica algo, en vez de modificar directamente desactiva esa fila y crea una nueva, para lo cual hay que consultar no solo las activas sino todas. La pregunta es, como estructuro las capas? Yo lo habia pensado de la siguiente manera:
DAL: Traer datos activos a DataTables y todos los datos, guardar datos
BLL: Comprobar los valores, Realizar las desactivaciones y crear las nuevas filas
Presentacion:mostrar los datos.

Me surge la duda de si realizar las desactivaciones y crear las nuevas filas en los datatables va en BLL o iria en DAL y en ese caso que iría en BLL

Juan Manuel Herrera dijo...

Amigo la idea es que dejes las operaciones básicas de persistencia del lado de la capa de lógica de negocios (CRUD Opertations) y que orientando tu desarrollo a objetos manipules la lógica orientandolo al dominio del negocio representado en objetos según su responsabilidad y menos a a nivel de filas y columnas de una base de datos relacional. Así aprovechas la rehusabilidad, abstración y polimorfismo en la programación orientada a objetos, algún que puede ayudarte son los patrones de diseño orientado a objetos.

Saludos,

Manolo Herrera

Unknown dijo...

Esta bueno el ejemplo, yo quise aplicarlo haciendo que la solucion se forme por 3 proyectos cada uno representa una capa. El problema que tengo es el de las referencias ya que por ejemplo si en el proyecto Reglas hago referencia al proyecto Datos entonces en el proyecto Datos no puedo hacer referencia al proyecto Reglas porque seria una referencia circular. Como puedo hacer para arreglar esto ? Supongo que en tu ejemplo trabajas siempre en un proyecto.

Juan Manuel Herrera dijo...

No trabajo con un solo proyecto, es mas manejo mas de 3 proyectos cuando solo sean tres capas. Lo que debes de hacer es asociar el código de tal forma que no te suceda esto. Recuerda que la capa de datos no deberías tener ninguna lógica sino solo las operaciones CRUD (Create, Read, Update, Delete). Te doy un ejemplo:

Proyecto 1: ProyectoX.Core (Codigo común en varias capas funcionalidades que puedan estar presentes en dos o mas capas).
Proyecto 2: ProyectoX.Dominio ( Las entidades de negocios, Interfases de los servicios que son instanciadas por Datos, Logica y Presentación).
Proyecto 3: ProyectoX.Datos (Operaciones CRUD).
Proyecto 4: ProyectoX.Servicios (La lógica del negocio).
Proyecto 5: ProyectoX.Web (Capa de Presentación para Web).

Saludos,


Manolo Herrera

Danny dijo...

Muy buen articulo, con una consulta, cuando se utiliza LINQ/JQUERY, como cambiaria la arquitectura, supongo que se puede utilizar las 2 maneras en la misma solución, como seria una buena practica en este caso?

Danny dijo...

Muy buen articulo, con una consulta, cuando se utiliza LINQ/JQUERY, como cambiaria la arquitectura, supongo que se puede utilizar las 2 maneras en la misma solución, como seria una buena practica en este caso?

Juan Manuel Herrera dijo...

Buena pregunta!. Lo que debes de tener cuidado con Linq es si lo haces sobre Entity Framework o el ya descotinuado LinqtoSql no hay mayor problema porque esto se ejecuta al final en el proveedor de datos optimizando el retorno de los elementos de la base de datos, pero si estas utilizando Entidades de Negocio y vas a filtrar un gran volumen de información en la lógica de negocios vas a tener un problema de rendimiento. Con jquery definitivamente pertenece a la capa de presentación. Recuerda no importa lo que validez o no en la presentación en tu lógica de neogcio debes de colocar las relgas de negocio para garantizar que lo que persistas en el Repositorio es información valida y util para le negocio.

Anónimo dijo...

Hace tiempo vengo buscando algo de transacciones entre capas, si actualmente tenemos TransaccionScope pero tengo la duda desde hace tiempo como hacerlo sin eso y hacerlo unicamente con el Objeto Transaction, podrias poner un ejemplo simple de como insertar en dos tablas distintas cada una en un proyecto distinto como entidades separadas pero que al insertar en las dos se pueda hacer por una transacción sin transaccionscope?? Ojala se pueda.. Gracias...Max

Arévalo Ignacio dijo...

Hola a todos! Hola Manolo! Mira estoy con linqtosql el cual, como ya sabras, tiene metodos de validacion como OnCreated(), OnValidate() etc... si los uso no estaria dejando ese trabajo a la capa de negocios, lo cual no se si esta bien. Te comento como resuelvo el problema haber que opinan:

1) En la capa DataAccessLayer tengo los metodos basicos CRUD y uso linq. En ocasiones uso los metodos de validacion antes mencionados cuando necesito ingresar a la base de datos para validar algun parametro y genero excepciones si algo no esta bien.

2) En BusinessLayer valido ciertos datos, como por ejemplo que ciertos objetos no sean null etc. y llamo a los metodos de DataAccessLayer y tomo las excepciones incluso las de submitchanges().

3) En la capa PresentationLayer solo tomo los valores de entrada, llamo a BusinessLayer y obtengo los valores resultado

Eso es todo. Queria saber que opinan al respecto...
Gracias

Juan Manuel Herrera dijo...

Estimado Ignacio:
El propósito de la cada capa es aislar su propósito del de los demas. El de la capa de acceso a datos es asilar el proveedor de datos cualquiera que este sea del problema del negocio que estas resolviendo para dejar libre tu modelo del negocio. Así que lo que piensas hacer me parece bien, pero no desccartes la posibilidad de realizar validaciones en cada capa según sea el caso necesario para cada una. Una validación en la capa de presentación no excluye la validación del negocio o del acceso a datos. Por ejemplo:
Puedes validar en la capa de presentación que no olbigatorio ingresar un valor en un textbox y que este sea un valor númerico. En la capa de negocios validas que el valor ingersado sea la cuenta valida del banco y en la capa de acceso a datos que no sea un valor nulo y que sea un valor int32 por ejemplo.

Saludos y espero a verte ayudado en tu duda.