Вы находитесь на странице: 1из 15

Autor: Daniel Andrade Fecha: 26 de Julio 2007

Arquitectura orientada a Servicio


Introduccin
Esta arquitectura debe ser usada para aplicaciones distribuidas, se aconseja implementarla con WCF (Windows Communication Foundation) para dar una mayor flexibilidad al tipo de formato de mensajes y transporte que se pueda utilizar pero tambin puede utilizarse con XML Web Services comunes y corrientes. En la figura 1 podemos visualizar el diseo fsico ms bsico de esta arquitectura, consta de 2 partes (Cliente y Servidor) que se comunican entre si mediante mensajes y la utilizacin de Servicios WCF o de XML Web Services.

Figura 1. Diseo Fsico de la Arquitectura

En escenarios ms complejos la arquitectura podra ser implementada en 2 o 3 servidores (Servidor de Interfase de Servicio, Servidor de Implementacin de Servicios y Servidor de Base de Datos). La idea fundamental de esta arquitectura es independizar la interfaz de usuario del Backend logrando de sta manera fcilmente intercambiar las interfaces entre aplicaciones Windows, Web o clientes inteligentes. Adicionalmente nos permiten enfocar los cambios que podran darse en los requerimientos a porciones pequeas de cdigo logrando disminuir el impacto dentro del desarrollo de nuestras aplicaciones.

En la figura 2 muestra visualizar los componentes y capas utilizadas para implementar la arquitectura as como tambin los componentes comunes que son utilizados por todas las dems capas (Figura 3).

Figura 2. Capas y Componentes utilizados por la Arquitectura

Figura 3. Componentes comunes a toda la aplicacin

Capas y Componentes utilizadas


Entidades de Negocio
En este componente encontraremos las representaciones de las entidades de nuestra base de datos as como tambin sus mecanismos y utileras para manejar sus estados internos. Por lo general existirn 2 entidades de negocio por cada entidad de nuestra base (Una para manejar datos nicos y otra que actuar como coleccin para manejo de datos mltiples) Ej.: public class BancoEntity : EntityStatesManager { #region << Atributtes >> private int id; private string nombre; #endregion #region << Properties >> public int Id { get { return id; } set { id = value; } } public string Nombre { get { return nombre; } set

{ nombre = value; } }

#endregion

public BancoEntity () { SetNewState(); } } public class BancoEntityCollection : List<BancoEntity> { }

Mensajes
En este componente encontraremos los mensajes del tipo Request y Response que son necesarios para invocar a los metodos publicados por los Servicios web o WCF. Por lo general existirn 2 Mensajes por cada mtodo publicado por el servicio. Ej.: [XmlTypeAttribute(Namespace = "http://EIKON.Nomina.Mensajes/BancoSaveRequest")] [XmlRootAttribute(Namespace = "http://EIKON.Nomina.Mensajes/BancoSaveRequest", IsNullable = false)] public class BancoSaveRequest: RequestBase { private BancoEntity bancoEntity; public BancoEntity BancoEntity { get { return bancoEntity; } set { bancoEntity = value; } } } [XmlTypeAttribute(Namespace "http://EIKON.Nomina.Mensajes/BancoSaveResponse")] [XmlRootAttribute(Namespace "http://EIKON.Nomina.Mensajes/BancoSaveResponse", IsNullable = false)] public class BancoSaveResponse: ResponseBase { private BancoEntity bancoEntity; public BancoEntity BancoEntity { get { return bancoEntity; } set = =

{ bancoEntity = value; } } }

Utileras Comunes
Este componente manejar todas las acciones comunes que se puedan dar dentro de la aplicacin como conversin de monedas, unidades de medida, etc. Puede ser o un componente aislado o un Framewok de desarrollo utilizado por todos los proyectos de la compaa.

Interfaz de Usuario
Esta capa contendr todas nuestras interfaces graficas ya sean Windows, Web o Dispositivos inteligentes. Interactuar exclusivamente con las entidades de negocio, utilidades comunes y la cada de Adaptadores de Interfaz y por ningn otro motivo deber tener referencias a las dems capas. Ej:

internal bool CreateNewBanco() { try { BancoEntity Banco = new BancoEntity(); SeteaValoresDesdeControles(ref Banco); List<EIKON.Nomina.Comunes.Mensajes> errores = BancoAdapter.Save(ref Banco); if (errores != null) ManejaErroresBanco(errores); else { BancoEntityCollection BancoEntityCollection = (BancoEntityCollection)Session[StateBanco]; BancoEntityCollection.Add(Banco);

ListItem item = new ListItem(Banco.Nombre, Banco.Id.ToString()); lstBanco.Items.Add(item); lstBanco.SelectedIndex = lstBanco.Items.IndexOf(item); return true;

} } catch (Exception exc) { return false; } return false; } internal bool UpdateBanco()

{ try { if (lstBanco.SelectedIndex >= 0) { BancoEntityCollection BancoEntityCollection = (BancoEntityCollection)Session[StateBanco]; if (BancoEntityCollection != null) { BancoEntity Banco = BancoEntityCollection[lstBanco.SelectedIndex]; Banco.SetUpdatedState(); SeteaValoresDesdeControles(ref Banco);

List<EIKON.Nomina.Comunes.Mensajes> errores = BancoAdapter.Save(ref Banco); if (errores != null) ManejaErroresBanco(errores); else { lstBanco.SelectedItem.Text = Banco.Nombre;

SoloLecturaControlesBanco(true); SetCurrentState(BancoStateEnum.None); return true; }

} } } catch (Exception exc) { return false; } return false; }

protected void btnDeleteBanco_Click(object sender, ImageClickEventArgs e) { if (lstBanco.SelectedIndex >= 0) { BancoEntityCollection BancoEntityCollection = (BancoEntityCollection)Session[StateBanco]; if (BancoEntityCollection != null) { BancoEntity Banco = BancoEntityCollection[lstBanco.SelectedIndex]; Banco.SetDeletedState();

List<EIKON.Nomina.Comunes.Mensajes> errores = BancoAdapter.Save(ref Banco); if (errores != null) ManejaErroresBanco(errores); else { BancoEntityCollection.RemoveAt(lstBanco.SelectedIndex); lstBanco.DataSource = BancoEntityCollection; lstBanco.DataBind(); if (BancoEntityCollection.Count > 0) { lstBanco.SelectedIndex = 0; lstBanco_SelectedIndexChanged(null, null); } else { ReseteaControlesBanco(); divDatosBanco.Visible = false; } } } } }

Adaptadores de Interfaz de Usuario


Esta capa contendr la lgica necesaria para invocar un mtodo de un Servicio Web o WCF e interactuar exclusivamente con la capa de Mensajes, utilidades comunes, entidades de negocio y la capa de Proxy. Por lo general existirn 2 Adaptadores de negocio por cada entidad de la Base de Datos. Ej: public class BancoAdapter {

public static List<EIKON.Nomina.Comunes.Mensajes> Save(ref BancoEntity bancoEntity) { try { BancoSaveRequest request = new BancoSaveRequest(); request.BancoEntity = bancoEntity; BancoWebReference.BancoWebService service = new BancoWebReference.BancoWebService(); service.Url =UrlManager.GetUrl("BancoWebService"); BancoSaveResponse response = service.Save(request); if (!response.AccionExitosa) return response.Errores; bancoEntity = response.BancoEntity; } catch (Exception exc) { //TODO: Proveer algun mecanismo para manejar excepciones } return null;

public static void Load(ref BancoEntity bancoEntity) { try { BancoLoadRequest request = new BancoLoadRequest(); BancoWebReference.BancoWebService service = new BancoWebReference.BancoWebService(); service.Url = "http://localhost/NominaWebServices/BancoWebService.asmx"; BancoLoadResponse response = service.Load(request); bancoEntity = response.BancoEntity; } catch (Exception exc) { }

} }

Proxys
Esta capa contendr los proxys que permitan comunicar al cliente con los Servicios Web o WCF, puede ser implementado como un componente aparte o como clases dentro de la capa de Adaptares de interfaz.

Servicios
Esta capa contendr todos los Servicios Web o WCF que nuestra aplicacin necesite para manipulara la data de la base de datos o ejecutar determinada regla de negocio. Utilizar exclusivamente los componentes de Mensajes, Entidades, utileras comunes y la capa de Acciones de Negocio. Por lo general existirn 2 Servicios por cada entidad de la Base de Datos. Ej: [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class BancoWebService : System.Web.Services.WebService { public BancoWebService() { //Uncomment the following line if using designed components //InitializeComponent(); }

[WebMethod] public BancoSaveResponse Save(BancoSaveRequest request) { BancoSaveResponse response = new BancoSaveResponse(); try { response.BancoEntity = BancoBussinesAction.Save(request.BancoEntity, null); response.AccionExitosa = true; } catch(Exception exc) { Mensajes msg = new Mensajes(exc); response.Errores.Add(msg); } return response; }

[WebMethod] public BancoLoadResponse Load(BancoLoadRequest request) { BancoLoadResponse response = new BancoLoadResponse (); response.BancoEntity = BancoBussinesAction.Load(request.BancoEntity.Id, null); return response; }

Acciones de Negocio
Esta capa ser la encargada de manejar toda la lgica del negocio. Puede hacer uso de mtodos y clases contenidas dentro de su misma capa as como tambin la capa de acceso a datos. Ser la encargada de orquestar nuestros procesos para ejecutarlos dentro de una misma transaccin y puede interactuar adems con los componentes de Entidades de negocio y utileras comunes. Por lo general existirn 2 Acciones de Negocio por cada entidad de la Base de Datos. Ej: namespace EIKON.Nomina.AccionesNegocio { public class BancoBussinesAction { public static BancoEntity Load(int Id, SqlConnection conexion) { #region << Evaluo si es BA padre o hijo >> bool esBAPadre = false; if (conexion == null) { esBAPadre = true; conexion = new SqlConnection(ConfigDA.CadenaConexion);

} #endregion BancoEntity banco = new BancoEntity(); try { banco = BancoDataAccess.Load(Id, conexion); } catch (Exception exc) { throw exc; } finally { if (esBAPadre) conexion.Close(); } return banco; }

public static BancoEntity Save(BancoEntity banco , SqlConnection conexion) { #region << Evaluo si es BA padre o hijo >> bool esBAPadre = false; if (conexion == null) { esBAPadre = true; conexion = new SqlConnection(ConfigDA.CadenaConexion); } #endregion try { using (TransactionScope transaccion = new TransactionScope(TransactionScopeOption.Required)) { switch (banco.CurrentState) { case EntityStatesEnum.Deleted: BancoDataAccess.Delete(banco, conexion); break; case EntityStatesEnum.Updated: BancoDataAccess.Update(banco, conexion); break; case EntityStatesEnum.New: banco = BancoDataAccess.Insert(banco, conexion); break; default: break; } transaccion.Complete();

} //Final del Alcance de la transaccion return banco; } catch (Exception exc) { throw exc; } finally { if (esBAPadre) conexion.Close(); } }

} }

Acceso a Datos
Esta capa ser la encargada de interactuar con la Base de Datos. Por lo general existirn 2 Acciones de Negocio por cada entidad de la Base de Datos. Ej: public class BancoDataAccess { #region << Metodos por Defecto >> /// <summary> /// Crea una entidad del tipo Banco /// </summary> /// <param name= "banco">entidad a crear</param> /// <param name="conexion">conexion a la Base de Datos</param> /// <returns>Entidad creada</returns> public static BancoEntity Insert(BancoEntity banco, SqlConnection conexion) { SqlCommand mCommand = new SqlCommand(); try { mCommand.Connection = conexion; mCommand.CommandType = CommandType.StoredProcedure; mCommand.CommandText = "InsertBanco"; // Add the params mCommand.Parameters.AddWithValue("@NOMBRE", banco.Nombre); // Add the primary keys columns mCommand.Parameters.Add("@ID", SqlDbType.Int); mCommand.Parameters["@ID"].Direction = ParameterDirection.Output;

// Insert Banco if (conexion.State != ConnectionState.Open) conexion.Open();

mCommand.ExecuteNonQuery(); banco.Id = Convert.ToInt32(mCommand.Parameters["@ID"].Value);

return banco; } catch (Exception exc) { throw exc; } finally { mCommand.Dispose(); } } /// <summary> /// Actualiza una entidad del tipo Banco /// </summary> /// <param name="banco">entidad a actualizar</param> /// <param name="conexion">conexion a la Base de Datos</param> public static void Update(BancoEntity banco, SqlConnection conexion) { SqlCommand mCommand = new SqlCommand(); try { mCommand.Connection = conexion; mCommand.CommandType = CommandType.StoredProcedure; mCommand.CommandText = "UpdateBanco"; // Add the params mCommand.Parameters.AddWithValue("@ID", banco.Id); mCommand.Parameters.AddWithValue("@NOMBRE", banco.Nombre);

// Update banco if (conexion.State != ConnectionState.Open) conexion.Open(); mCommand.ExecuteNonQuery();

} catch (Exception exc) { throw exc; } finally { mCommand.Dispose(); } } /// <summary> /// Actualiza una entidad del tipo Banco /// </summary> /// <param name="banco">entidad a actualizar</param> /// <param name="conexion">conexion a la Base de Datos</param> public static void Delete(BancoEntity banco, SqlConnection conexion)

{ SqlCommand mCommand = new SqlCommand(); try { mCommand.Connection = conexion; mCommand.CommandType = CommandType.StoredProcedure; mCommand.CommandText = "DeleteBanco"; mCommand.Parameters.AddWithValue("@ID", banco.Id);

// Update banco if (conexion.State != ConnectionState.Open) conexion.Open(); mCommand.ExecuteNonQuery();

} catch (Exception exc) { throw exc; } finally { mCommand.Dispose(); } } /// <summary> /// Carga una entidad del tipo Pais /// </summary> /// <param name="id">id de Banco</param> /// <param name="conexion">conexion a la Base de Datos</param> /// <returns>Entidad cargada</returns> public static BancoEntity Load(int Id, SqlConnection conexion) { BancoEntity banco = new BancoEntity(); banco.Id = Id; SqlCommand mCommand = new SqlCommand(); SqlDataReader reader = null; try { mCommand.Connection = conexion; mCommand.CommandType = CommandType.StoredProcedure; mCommand.CommandText = "LoadBanco"; // Add the params mCommand.Parameters.AddWithValue("@ID", banco.Id);

if (conexion.State != ConnectionState.Open) conexion.Open(); reader = mCommand.ExecuteReader(); while (reader.Read()) { banco.Id = Convert.ToInt32(reader["ID"]); banco.Nombre = Convert.ToString(reader["NOMBRE"]); }

return banco; } catch (Exception exc) { throw exc; } finally { if (reader != null) reader.Close(); mCommand.Dispose(); } } #endregion }

Вам также может понравиться