Arquitectura de Aplicaciones para .NET
Básicamente la Arquitectura se centra en un arquitectura de 3 capas. 1. La capa capa de pres presen enta taci ción ón que que en este este caso caso esta esta for formada mada por por los 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 compon component entes es de proce proceso so de IU podría podríamo mos s asocia asociarlo rlos s a clases clases de tipo 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 ejercicio.. Las entidades entidades empresarial empresariales, es, que repre represent sentan an objetos que van a ser manejados o consumidos por toda la aplicación, esto estos s podr podríían ser un mode modelo lo de obj objetos etos,, xml xml, dat dataset asets s con con tipo tipo,, estruc estructur turas as de datos, datos, que permit permitan an repr represe esenta ntarr objeto objetos s que han sido sido identificados durante el modelamiento. Los otros tipos de objetos son los comp compon onen ente tes s empr empres esar aria iale les s que que cont contie iene nen n lógi lógica ca de nego negoci cio, o, y en algun lgunos os cas casos al usar COM+ OM+ son los obj objetos etos raíz aíz que inicia ician n 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 cohe cohesi sión ón o clas clases es alta altame ment nte e espe especi cial aliz izad adas as que que ayud ayuden en a reduc educir ir la dependencia entre las clases y capas. Aquí podemos encontrar también una clase con métodos estáticos que permiten uniformizar 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. 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 describir el problema, su diseño y su solución se esta usando UML, en este caso los diagramas que se usaron fueron, fueron, los casos de uso, diagramas de secuencia, diagramas de componentes, y diagramas de clases por tipos de componentes según la Arquitectura. 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 paqu paquet etes es que que se han han crea creado do para para refle efleja jarr la impl implem emen enta taci ción ón de la Arqu Arquit itec ectu tura ra , los los caso casos s de uso uso y los los diag diagra rama mas s de secu secuen enci cia. a. Aquí quí 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:
LIBN LIBNET ETBu Busi sine ness ssLo Logi gic c cont contie iene ne la clas clase e de la capa capa de nego negoci cio o que 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 empresarial iales, que estará implem lementado en el proyecto LIBNETBusinessEntity,, con el siguiente código: LIBNETBusinessEntity using System; namespace LIBNETBusinessEntity { public class CPedidoBE { private int iIdLibro; private int iCantidad; public int IIdLibro { get { return this.iIdLibro; this.iIdLibro; } set { this.iIdLibro= this .iIdLibro= value value;; } } public int ICantidad { get { return this.iCantidad; this.iCantidad; } set { this.iCantidad this .iCantidad = value value;; } } public CPedidoBE()
{ } public CPedidoBE(int CPedidoBE(int iIdLibro, int iCantidad) { this.iCantidad this .iCantidad = iCantidad; this.iIdLibro 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 lógica de los compon component entes es empre empresar saria iales les según según la Arqui Arquitec tectur tura. a. Esta Estará rá impl implem emen enta tada da por por el comp compon onen ente te LIBN LIBNET ETBu Busi sine ness ssLo Logi gic, c, 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)) if (oCPedidoD.RegistrarPedido(oCPedido)) { CLibroD oCLibroD = new CLibroD(); oCLibroD.ActualizarStock(oCPedido); return true; true; } else { return false; false; } } } }
El atri atribu buto to Au Auto toCo Comp mple lete te en el méto método do perm permit ite e esta establ blec ecer er que que este 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 BusinessEntities Aquí se llama a la clase de la capa de datos: CPedidoD oCPedidoD = new CPedidoD(); if (oCPedidoD.RegistrarPedido(oCPedido)) if (oCPedidoD.RegistrarPedido(oCPedido))
CLibroD oCLibroD = new CLibroD(); oCLibroD.ActualizarStock(oCPedido); return true; true;
- La capa capa de acce acceso so a dato datos s es impl implem emen enta tado do por por el comp compon onen ente te LIBNET LIB NETSQL SQLDat Data, a, que contie contiene ne dos clases clases Clibr ClibroD oD y Cpedi CpedidoD doD,, se han creado dos clases para aumentar el nivel de reusabilidad de la lógica al especi especiali alizar zar las clase clases. s. También ambién tengo tengo que hacer hacer notar notar el uso de los métodos estáticos dentro de la clase SqlHelper como una mejor practica para para incr increme ementa ntarr la estand estandari arizac zación ión dentr dentro o de nuestr nuestros os proye proyecto ctos s 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 System; using Microsoft.ApplicationBlocks.Data; using LIBNETBusinessEntity; using System.EnterpriseServices; using System.Runtime.InteropServices; namespace LIBNETData { [Transaction(TransactionOption.Supported)] [Guid("440FB96E-2EE6-489d-9ED2-F3E123456170")] [ConstructionEnabled(Default="server=localhost; [ConstructionEnabled(Defa ult="server=localhost;database=LIBNET2 database=LIBNET2003;uid=sa;pwd=;")] 003;uid=sa;pwd=;")] public class CLibroD:ServicedComponent { private string sCadenaConexion; protected override void Construct(string Construct(string s) { this.sCadenaConexion this .sCadenaConexion = s; } [AutoComplete] public bool ActualizarStock(CPedidoBE ActualizarStock(CPedidoBE oCPedido) { int iIdLibro = oCPedido.IIdLibro; oCPedido.IIdLibro; int iCantidad = oCPedido.ICantidad; oCPedido.ICantidad; try { SqlHelper.ExecuteNonQuery( this this.sCadenaConexion, .sCadenaConexion, "usp_ActualizarStockLibro",iIdLibro,iCantidad); return true; true; } catch { return false; false; } } public CLibroD() { // // TODO: Add constructor logic here // } } }
Notar el uso del Constructor String [ConstructionEnabled(Default [ConstructionEnabled(Default="server=loca ="server=localhost;database=LIBN lhost;database=LIBNET2003;ui ET2003;ui d=sa;pwd=;")] Y como se recupera su valor private string sCadenaConexion; protected override void Construct(string Construct(string s) { this.sCadenaConexion this .sCadenaConexion = s; }
El método también se ejecuta dentro de la transacción [AutoComplete] public bool ActualizarStock(CPedidoBE ActualizarStock(CPedidoBE oCPedido) { int iIdLibro = oCPedido.IIdLibro; oCPedido.IIdLibro; int iCantidad = oCPedido.ICantidad; oCPedido.ICantidad; try { SqlHelper.ExecuteNonQuery( this this.sCadenaConexion, .sCadenaConexion, "usp_ActualizarStockLibro",iIdLibro,iCantidad); return true; true; } catch { return false; false; }
Para simplificar y uniformizar el acceso a datos se hace uso de la clase SqlHelper del DataAccessApplicationBlo DataAccessApplicationBlock ck SqlHelper.ExecuteNonQuery( this this.sCadenaConexion, .sCadenaConexion, "usp_ActualizarStockLibro",iIdLibro,iCantidad); return true; 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=LIBNET2 [ConstructionEnabled(Default="server=localhost; database=LIBNET2003;uid=sa;pwd=;")] 003;uid=sa;pwd=;")] public class CPedidoD:ServicedComponent { private string sCadenaConexion; protected override void Construct(string Construct(string s) { this.sCadenaConexion this .sCadenaConexion = s; } [AutoComplete] public bool RegistrarPedido(CPedidoBE oCPedido) { int iIdLibro = oCPedido.IIdLibro; oCPedido.IIdLibro; int iCantidad = oCPedido.ICantidad; oCPedido.ICantidad; try {
SqlHelper.ExecuteNonQuery( this this.sCadenaConexion, .sCadenaConexion, "usp_AgregarPedido",iIdLibro,iCantidad); return true; } catch(Exception catch (Exception e) { return false; false; } } public CPedidoD() } }
2. Dentro de los archivos de este ejemplo he incluido una versión de LIBN LIBNET ET que que hace hace uso uso de un "Fac "Facto tory ry"" para para sepa separa rarr inde indepe pend ndiz izar ar 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 base de dato datos. s. El fact factor ory y actú actúa a como como un crea creado dorr de obje objeto tos, s, deci decidi dien endo do cual cual es el obje objeto to a crea crearr de acue acuerrdo a pará paráme metr tros os de config configura uració ción. n. Para que el client cliente e pueda pueda usar usar cualqu cualquier ier objeto objeto,, estos estos objeto objetos s implem implement entan an una inter interfaz faz genéri genérica. ca. De esta esta forma forma el factor factory y retor retorna na esta esta interf interfaz, az, y a través través de polimo polimorfi rfismo smo dinám dinámico ico,, el client cliente e 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 ActualizarStock(CPedidoBE oCPedido); } }
ICPedidoD.cs using System; using LIBNETBusinessEntity; namespace LIBNETIData { public interface ICPedidoD { bool RegistrarPedido(CPedidoBE RegistrarPedido(CPedidoBE oCPedido); } }
El proye proyecto cto LIB LIBNET NETIF IFact actory oryIDa IData ta contie contiene ne las las llama llamadas das a los objeto objetos s adecuados de acuerdo a una configuración en el Web.config
LIBNETIFactoryIData LibroFactory.cs using System; using System.Reflection; 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 correcto aquí. public static ICLibroD Crear() { string path = System.Configuration.Configurati System.Configuration.ConfigurationSettings.AppSet onSettings.AppSettings["WEBDat tings["WEBData"]; a"]; 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 esto con con la Arqu Arquit itec ectu tura ra de Ap Apli lica caci cion ones es de Micr Micros osof oftt para para .NET .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 desarr desarroll ollo o apoyad apoyada a por una herram herramien ienta ta Case Case como como Visi Visio o Enterp Enterpri rise se Architect, más mejores prácticas (patrones, building blocks, patrones de Arquitectura), Arquitectura), y la implementación usando VS .NET 2003. Muchos Muchos concep conceptos tos como como Servic Service e Orient Oriented ed Archi Architec tectur ture e o Model Model Driven Driven Architecture Architecture me han servido de referencia para llevar a cabo este ejercicio. ejercicio.