Arquitectura de Aplicaciones para .

NET

Básicamente la Arquitectura se centra en un arquitectura de 3 capas. 1. La capa de presentación que en este caso esta formada por los Componentes de IU, y los componentes de proceso de IU. Los componentes de IU pueden ser vistos como la parte con la cual interactuar el usuario. Las ventanas o páginas web, por decirlo de alguna manera. Los componentes de proceso de IU podríamos asociarlos a clases de tipo controladora en UML. Es decir estos encapsulan lógica de navegación y control de eventos de la interfase. 2. La capa de negocios encapsula lógica de negocios. Los servicios de esta capa son encapsulados en tres tipos de componentes, dos de los cuales se tocan en este ejercicio. Las entidades empresariales, que representan objetos que van a ser manejados o consumidos por toda la aplicación, estos podrían ser un modelo de objetos, xml, datasets con tipo, estructuras de datos, que permitan representar objetos que han sido identificados durante el modelamiento. Los otros tipos de objetos son los componentes empresariales que contienen lógica de negocio, y en algunos casos al usar COM+ son los objetos raíz que inician las transacciones. 3. La capa de acceso a datos que contiene clases que interactúan con la base de datos. Estas clases surgen como una necesidad de mantener la cohesión o clases altamente especializadas que ayuden a reducir la dependencia entre las clases y capas. Aquí podemos encontrar también una clase con métodos estáticos que permiten uniformizar las operaciones

de acceso a datos a través de un único conjunto de métodos, esta clase es el SQLHelper que también se usa en este proyecto LIBNET sigue esta Arquitectura, implementando las tres capas. 1. LIBNET implementa un site de ventas de libros. Esta página representa la capa de presentación.

Esta es la ventana de la Aplicación. Se ingresa el código de libro y la cantidad. Cuando se presionar el botón registrar se genera el Pedido. Para llevar a cabo la generación del Pedido se hace uso de COM+. Para describir el problema, su diseño y su solución se esta usando UML, en este caso los diagramas que se usaron fueron, los casos de uso, diagramas de secuencia, diagramas de componentes, y diagramas de clases por tipos de componentes según la Arquitectura. El material de este Hands On contiene un documento en Visio llamado LIBNET.vsd que contiene el caso de uso a implementar, el diagrama de secuencia, el bosquejo de la Arquitectura y las clases en cada una de las capas y componentes, con diagramas UML

Esta es la parte del documento de Visio que muestra la estructura de los paquetes que se han creado para reflejar la implementación de la Arquitectura , los casos de uso y los diagramas de secuencia. Aquí decidimos colocar el diagrama de secuencia en otro paquete para tener una mejor claridad. La Arquitectura de LIBNET se muestra en el siguiente gráfico:

LIBNETBusinessLogic contiene la clase de la capa de negocio que implementan la transacción COM+. En este caso se esta usando COM+ para manejar las transacciones. - Capa de Negocios, aquí tenemos inicialmente a las entidades empresariales, que estará implementado en el proyecto LIBNETBusinessEntity, con el siguiente código:
using System; namespace LIBNETBusinessEntity { public class CPedidoBE { private int iIdLibro; private int iCantidad; public int IIdLibro { get { } set { } return this.iIdLibro;

this.iIdLibro= value;

} public int ICantidad { get { return this.iCantidad; } set { this.iCantidad = value; } } public CPedidoBE()

{ } public CPedidoBE(int iIdLibro, int iCantidad) { this.iCantidad = iCantidad; this.iIdLibro = iIdLibro; } } }

Como se puede ver LIBNETBusinessEntity solo contiene propiedades y un constructor, que nos proporciona los objetos que van a contener los datos que van a pasar a través de las capas. De esta manera todas las capas conocen la estructura, a través de esta clase - La lógica de los componentes empresariales según la Arquitectura. Estará implementada por el componente LIBNETBusinessLogic, y el objetivo es que contenga la lógica de negocios, y en este caso al usar COM+ inicien las transacciones
using System; using LIBNETData; using LIBNETBusinessEntity; using System.EnterpriseServices; using System.Runtime.InteropServices; namespace LIBNETBusinessLogic { [Transaction(TransactionOption.Required)] [Guid("440FB96E-2EE6-489d-9ED2-F3E2456C0170")] public class CPedidoBT:ServicedComponent { public CPedidoBT() { } [AutoComplete] public bool RegistrarPedido(CPedidoBE oCPedido) { CPedidoD oCPedidoD = new CPedidoD(); if (oCPedidoD.RegistrarPedido(oCPedido)) { CLibroD oCLibroD = new CLibroD(); oCLibroD.ActualizarStock(oCPedido); return true; } else { return false; } } } }

El atributo AutoComplete en el método permite establecer que este método RegistrarPedido es transaccional.
public bool RegistrarPedido(CPedidoBE oCPedido)

El método recibe como argumento el obteo ocPedido que es una instancia de la clase CpedidoBE que ha sido definido dentro de los BusinessEntities Aquí se llama a la clase de la capa de datos:
CPedidoD oCPedidoD = new CPedidoD(); if (oCPedidoD.RegistrarPedido(oCPedido))

CLibroD oCLibroD = new CLibroD(); oCLibroD.ActualizarStock(oCPedido); return true;

- La capa de acceso a datos es implementado por el componente LIBNETSQLData, que contiene dos clases ClibroD y CpedidoD, se han creado dos clases para aumentar el nivel de reusabilidad de la lógica al especializar las clases. También tengo que hacer notar el uso de los métodos estáticos dentro de la clase SqlHelper como una mejor practica para incrementar la estandarización dentro de nuestros proyectos de desarrollo a través de todo el equipo de trabajo. Esta es la definición de la clase de acceso a datos ClibroD
using using using using using System; Microsoft.ApplicationBlocks.Data; LIBNETBusinessEntity; System.EnterpriseServices; System.Runtime.InteropServices;

namespace LIBNETData { [Transaction(TransactionOption.Supported)] [Guid("440FB96E-2EE6-489d-9ED2-F3E123456170")] [ConstructionEnabled(Default="server=localhost;database=LIBNET2003;uid=sa;pwd=;")] public class CLibroD:ServicedComponent { private string sCadenaConexion; protected override void Construct(string s) { this.sCadenaConexion = s; } [AutoComplete] public bool ActualizarStock(CPedidoBE oCPedido) { int iIdLibro = oCPedido.IIdLibro; int iCantidad = oCPedido.ICantidad; try { SqlHelper.ExecuteNonQuery(this.sCadenaConexion, "usp_ActualizarStockLibro",iIdLibro,iCantidad); return true; } catch { return false; } } public CLibroD() { // // TODO: Add constructor logic here // } } }

Notar el uso del Constructor String [ConstructionEnabled(Default="server=localhost;database=LIBNET2003;ui d=sa;pwd=;")] Y como se recupera su valor
private string sCadenaConexion; protected override void Construct(string s) { this.sCadenaConexion = s; }

El método también se ejecuta dentro de la transacción
[AutoComplete] public bool ActualizarStock(CPedidoBE oCPedido) { int iIdLibro = oCPedido.IIdLibro; int iCantidad = oCPedido.ICantidad; try { SqlHelper.ExecuteNonQuery(this.sCadenaConexion, "usp_ActualizarStockLibro",iIdLibro,iCantidad); return true; } catch { return false; }

Para simplificar y uniformizar el acceso a datos se hace uso de la clase SqlHelper del DataAccessApplicationBlock
SqlHelper.ExecuteNonQuery(this.sCadenaConexion, "usp_ActualizarStockLibro",iIdLibro,iCantidad); return true;

La otra clase Cpedido tiene una estructura parecida:
using System; using Microsoft.ApplicationBlocks.Data; using LIBNETBusinessEntity; using System.EnterpriseServices; using System.Runtime.InteropServices; namespace LIBNETData { [Transaction(TransactionOption.Supported)] [Guid("440FB96E-2EE6-489d-9ED2-4444456C0170")] [ConstructionEnabled(Default="server=localhost;database=LIBNET2003;uid=sa;pwd=;")] public class CPedidoD:ServicedComponent { private string sCadenaConexion; protected override void Construct(string s) { this.sCadenaConexion = s; } [AutoComplete] public bool RegistrarPedido(CPedidoBE oCPedido) { int iIdLibro = oCPedido.IIdLibro; int iCantidad = oCPedido.ICantidad; try {

SqlHelper.ExecuteNonQuery(this.sCadenaConexion, "usp_AgregarPedido",iIdLibro,iCantidad); return true; } catch(Exception e) { return false; }

} }

} public CPedidoD()

2. Dentro de los archivos de este ejemplo he incluido una versión de LIBNET que hace uso de un "Factory" para separar independizar la aplicación del acceso a datos. Esto es algo parecido a lo que se hace en el PetShop. Un factory se usa cuando por alguna razón los objetos de un lado no pueden llamar directamente a los objetos del otro lado (en términos simples esta es una buena descripción). En esta caso, los objetos que no se pueden llamar directamente son los de la capa de datos. Si se llamaran directamente la aplicación se haría dependiente de la clase y por lo tanto de la base de datos. El factory actúa como un creador de objetos, decidiendo cual es el objeto a crear de acuerdo a parámetros de configuración. Para que el cliente pueda usar cualquier objeto, estos objetos implementan una interfaz genérica. De esta forma el factory retorna esta interfaz, y a través de polimorfismo dinámico, el cliente obtiene siempre el objeto adecuado con los mismos métodos, consiguiendo la independencia de las clases de acceso a datos y por lo tanto de la base de datos. Esta versión se llama LIBNETPetShop

El proyecto LIBNETIData contiene la definición de las interfaces. Una para cada clase, Pedido y Libro ICLibroD.cs
using System; using LIBNETBusinessEntity; namespace LIBNETIData { public interface ICLibroD { bool ActualizarStock(CPedidoBE oCPedido); } }

ICPedidoD.cs
using System; using LIBNETBusinessEntity; namespace LIBNETIData { public interface ICPedidoD { bool RegistrarPedido(CPedidoBE oCPedido); } }

El proyecto LIBNETIFactoryIData contiene las llamadas a los objetos adecuados de acuerdo a una configuración en el Web.config <appSettings> <add key="WEBData" value="LIBNETSQLData" /> </appSettings> LIBNETIFactoryIData LibroFactory.cs
using System; using System.Reflection; using LIBNETIData; namespace LIBNETFactoryIData { public class LibroFactory { public static ICLibroD Crear() { string path= system.configuration.ConfigurationSettings.AppSettings["WEBData"]; string NombreClase = path + ".CLibroD"; return (ICLibroD) Assembly.Load(path).CreateInstance(NombreClase); } } }

Al hacer uso de las interfaces se hace la llamada al objeto correcto aquí. public static ICLibroD Crear() { string path = System.Configuration.ConfigurationSettings.AppSettings["WEBData"]; string NombreClase = path + ".CLibroD"; return (ICLibroD) Assembly.Load(path).CreateInstance(NombreClase); }

Conclusión:
En este laboratorio se trata de mostrar el proceso de desarrollo formal de software a partir del análisis de requerimientos usando Casos de Uso, luego el diseño con UML, los diagramas de secuencia. Luego como enlazar esto con la Arquitectura de Aplicaciones de Microsoft para .NET, y finalmente como implementar un "patrón" dentro de la aplicación para independizarla del acceso a datos. En resumen, Una metodología sólida de desarrollo apoyada por una herramienta Case como Visio Enterprise Architect, más mejores prácticas (patrones, building blocks, patrones de Arquitectura), y la implementación usando VS .NET 2003. Muchos conceptos como Service Oriented Architecture o Model Driven Architecture me han servido de referencia para llevar a cabo este ejercicio.