Welcome to Kartones.Net Sign in

El framework de coco

No te acostaras sin saber una o dos cosas mas ...
La Capa de Presentación : El Patrón MVP

El patrón Modelo Vista Presentador (MVP) es un patrón derivado del patrón Modelo Vista Controlador (MVC) que nos ayuda a ofrecer una clara separación entre la vista, el modelo y el controlador.

La clave del patrón MVP es una estricta regulación de la interacción entre la vista y el controlador, aunque en el patrón MVP, al controlador se le conoce como presentador.

La vista y el modelo de datos son claramente separados a través de un contrato (interfaz) que expone la vista y al cual el presentador accede de modo polimórfico.

Resumiendo, podemos decir que el patrón MVP es una mejora del patrón MVC basado en tres características :

  • La vista no conoce el modelo.
  • El presentador es independiente de la tecnología de interfaz de usuario.
  • La vista y el presentador es testeable puesto que esta basada en un contrato.

Mediante el uso de un contrato en la vista, podemos testear la propia vista y el presentador de modo independiente :

patronMVP[1]

En aplicaciones web ASP.NET, las vistas se representan a través de formularios web, y en aplicaciones Windows a través de formularios Windows.

En la implementación de la vista, tenemos dos opciones :

  1. Una vista pasiva
  2. Una vista activa

Si optamos por una vista pasiva, obtendremos un mayor grado de testeabilidad por que la lógica representada por el contrato se reducirá al mínimo, sin embargo aumentaremos la complejidad en el presentador.

Si optamos por una vista activa que contiene lógica como el enlace y el formateo de datos, necesitaremos implementar una funcionalidad extra que convertirá la vista en menos testeable.

El presentador es el componente encargado de “presentar” las acciones del usuario al sistema y obtener la respuesta del mismo.

El presentador se ubica entre la vista y el modelo, y actúa de la siguiente manera :

  1. Recibe datos de la vista
  2. Convierte los datos de la vista en acciones que se ejecutan contra el sistema
  3. Con la respuesta del sistema, actualiza los datos de la vista.

Las acciones del usuario que requieran navegación a otra vista, deberán ser delegadas en el presentador, el cual será responsable de aplicar la redirección a la vista correspondiente.

El patrón MVP, es un patrón que no puede ser implementado sin haber hecho un análisis  previo que determine que miembros deben formar parte del contrato de cada vista.

Una vez que tenemos claro el contrato que debe implementar cada vista, podemos desarrollar la lógica de presentación de forma paralela al diseño gráfico.

El patrón MVP puede ser caro de implementar en aplicaciones relativamente sencillas, sin embargo brilla en escenarios de grandes aplicaciones.

SecuenciaMVP[1]

En un escenario de patrón Modelo Vista Presentador, lo primero que debemos hacer es definir el contrato de cada vista, cada formulario tiene su propia interfaz para hablar con el presentador correspondiente.

El contrato de la vista identifica el modelo de datos que soporta la vista.

Veamos un ejemplo :

EjemploVistaMVP[1]

La funcionalidad de la vista de la imagen anterior, esta basada en permitir la edición y el alta de empleados, partiendo de esta base, podemos implementar el siguiente contrato para la vista :

public interface IVistaEmpleado
{
    int IDEmpleado { get; set; }

    int IDCentro { get; set; }

    string Nombre { get; set; }

    string Apellido1 { get; set; }

    string Apellido2 { get; set; }

    int IDCategoria { get; set; }

    int IDDepartamento { get; set; }

    int IDEmpleadoResponsable { get; set; }
}

El uso de modificadores “get” y “set” en los miembros de la interfaz, dependerá de las características que queremos que soporte la vista, en nuestro ejemplo la vista será utilizada para modificar y dar de alta empleados, por tanto necesitaremos ambos modificadores.

El siguiente fragmento de código representa la implementación de la vista mediante un formulario ASP.NET :

public partial class frmEditarEmpleado : Page, IVistaEmpleado
{
    #region IVistaEmpleado Members

    public int IDEmpleado
    {
        get { return int.Parse(txtIDEmpleado.Text); }
        set { txtIDEmpleado.Text = value.ToString(); }
    }

    public int IDCentro
    {
        get { return int.Parse(ddlIDCentro.SelectedValue); }
        set { ddlIDCentro.SelectedValue = value.ToString(); }
    }

    public string Nombre
    {
        get { return txtNombre.Text; }
        set { txtNombre.Text = value; }

    }

    public string Apellido1
    {
        get { return txtApellido1.Text; }
        set { txtApellido1.Text = value; }
    }

    public string Apellido2
    {
        get { return txtApellido2.Text; }
        set { txtApellido2.Text = value; }
    }

    public int IDCategoria
    {
        get { return int.Parse(ddlIDCategoria.SelectedValue); }
        set { ddlIDCategoria.SelectedValue = value.ToString(); }
    }

    public int IDDepartamento
    {
        get { return int.Parse(ddlIDDepartamento.SelectedValue); }
        set { ddlIDDepartamento.SelectedValue = value.ToString(); }
    }

    public int IDEmpleadoResponsable
    {
        get { return int.Parse(ddlIDResponsable.SelectedValue); }
        set { ddlIDResponsable.SelectedValue = value; }
    }

    #endregion

    protected void Page_Load(object sender, EventArgs e)
    {

    }
}

Cuando la vista se carga por primera vez, se debe crear una instanciar del presentador y guardarla internamente en un campo privado :

public partial class frmEditarEmpleado : Page, IVistaEmpleado
{
    private PresentadorEmpleado presentador;

    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);
        presentador = new PresentadorEmpleado(this);
    }
    
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            presentador.InicializarVista();

            if (Request["Id"] != null)
            {
                int IdEmpleado = int.Parse(Request["Id"]);

                presentador.CargarEmpleado(IdEmpleado);
            }
        }
    }

    // [ IVistaEmpleado Members ]
}

Como podemos observar la vista contiene una referencia al presentador, y el presentador contiene una referencia a la vista.

La mejor forma de crear una clase presentador es con los siguientes miembros :

  • Un método de inicialización
  • Un método de actualización de la vista
  • Un conjunto de métodos que ejecutan métodos de la capa de servicios / negocio

Veamos la implementación de nuestro presentador :

public class PresentadorEmpleado
{
    private readonly IVistaEmpleado _vista;

    public PresentadorEmpleado(IVistaEmpleado vista)
    {
        _vista = vista;
    }

    public void CargarEmpleado(int IdEmpleado)
    {
        // Llamada a la capa de servicios / negocio ..
        Empleado emp = RepositorioEmpleado.Obtener(IdEmpleado);

        ActualizarVista(emp);
    }

    public void InicializarVista()
    {
        _vista.Apellido1 = string.Empty;
        _vista.Apellido2 = string.Empty;

        // ..
    }

    public void ActualizarVista(Empleado empleado)
    {
        _vista.Apellido1 = empleado.Apellido1;
        _vista.Apellido2 = empleado.Apellido2;
        _vista.IDCategoria = empleado.Categoria.ID;

        // ..
    }
}

Como se puede observar, el presentador cuelga una referencia a la vista y desconoce por absoluto si se trata de una vista ASP.NET o una vista WinForms, todo lo que sabe es que tiene una referencia a un objeto que implementa una interfaz.

El presentador lee y escribe el estado de la vista a través de los miembros de la interfaz.

A través de la interfaz de la vista, el presentador recoge todos los datos que necesita para ejecutar la acción solicitada en la capa de servicios / negocio.

Para actualizar la vista, el presentador solo tiene que asignar nuevos valores a las propiedades expuestas por la interfaz de la vista.

Para ayudarnos a implementar el patrón MVP, podemos apoyarnos en los componentes y librerías reusables de Web Client Software Factory (WCSF) ya que nos permiten aplicar varias prácticas y patrones en aplicaciones ASP.NET.

Podemos descargar Web Client Software Factory desde esta URL :

http://webclientguidance.codeplex.com/

Como comentamos antes, una de las responsabilidades del presentador es exponer funcionalidad para la navegación entre vistas, uno de los beneficios de usar WCSF, es que disponemos de un bloque para implementar flujos de interfaz de usuario a través de formularios web.

Mediante el bloque PFAB (Page Flow Application Block) podemos orquestar la secuencia de formularios web que necesitamos para completar los flujos de interfaz de usuario.

El bloque PFAB de Web Client Software Factory está basado en Windows WorkFlow Foundation y Enterprise Library.

Y eso ha sido todo por hoy ..

Posted: Dec 19 2009, 03:35 PM by ccrego | with no comments
La Capa de Presentación : Conceptos Básicos

A día de hoy no conozco ninguna aplicación que pueda utilizarse sin una interfaz de usuario, y aunque parezca curioso, muchos arquitectos tienden a encasillar a la interfaz de usuario como la parte más aburrida en el desarrollo y sin embargo en muchos proyectos es donde más tiempo de emplea.

La capa de presentación es a menudo la última parte que se despliega y tiende a ser muy dependiente de las herramientas de desarrollo utilizadas, sin embargo la capacidad de desenchufar una interfaz de usuario y reemplazarla con otra suele ser un requerimiento clave en todas las capas de presentación.

La implementación de un capa de presentación puede ser muy sencilla, pero hay escenarios donde se puede volver muy compleja mediante el uso de maestros, detalles y flujos de navegación.

A grandes rasgos se podría decir que la capa de presentación se compone de :

  1. La interfaz de usuario
  2. La lógica de presentación

La interfaz de usuario ofrece a los usuarios información, sugerencias, acciones y captura los datos de entrada a través del teclado y el ratón.

La lógica de presentación hace referencia a todo el procesamiento requerido para mostrar datos y transformar los datos de entrada en acciones que podemos ejecutar contra la capa de negocio o de servicios.

Se podría decir que la lógica de presentación esta muy relacionada con mostrar datos en la pantalla del usuario.

 DiagramaCapaPresentacion.JPG

Como se puede observar en la imagen, la capa de presentación es la interfaz entre el usuario y el sistema.

Si le preguntamos a alguien sobre cuales son las responsabilidades de la capa de presentación seguramente nos responderá con algunas de estas :

  • Validación
  • Formateo
  • Estilos
  • Usabilidad

El estilo y la usabilidad pueden considerarse simples atributos, y la validación y formateo pueden implementarse mediante componentes.

Todas estas “responsabilidades” suelen estar estrechamente relacionadas con la tecnología y plataforma elegida para implementar el sistema.

Una capa de presentación a alto nivel tiene tres grandes responsabilidades :

  1. Independencia de la interfaz de usuario
  2. Testeabilidad
  3. Independencia del modelo de datos

Seguramente nos habremos encontrado más de una vez con el siguiente dilema :

  • ¿ Enlace o Botón ?
  • ¿ Qué Colores uso ?
  • ¿ Qué bordes, estilos y fuentes ?

El caso es que la capa de presentación debe ser capaz de escalar todas estas incognitas sin que la lógica de presentación se vea afectada, un claro ejemplo de ello son las aplicaciones de blogs como el que estais leyendo ahora mismo.

La capa de presentación también debe ser independiente de la tecnología de interfaz de usuario y plataforma usadas, aunque este requerimiento es muy complicado de cumplir y en la mayoría de las ocasiones es imposible obtener una independencia total.

Para conseguir desacoplar la interfaz de usuario de la lógica de presentación  debemos hacer uso de contratos de datos, para entenderlo mejor imaginemos el siguiente formulario web de visualización y entrada de datos :

EjemploInterfazUsuario.JPG

A grandes rasgos, tenemos :

  • Datos de empleado
  • Datos de prendas y tallas disponibles
  • Datos de entrada

Si aplicamos un buen diseño en la capa de presentación nuestro contrato de datos para el formulario estará formado por la siguiente interfaz :

public interface IViewGarmentIncidence
{
    Employee Employee { get; set; }

    IList<Garment> Garments { get; set; }

    int IncidenceGarmentType { get; set; }

    int GarmentType { get; set; }

    int Size { get; set; }

    string Details { get; set; }

    int Amount { get; set; }

    string Comments { get; set; }
}

Mediante el uso de contratos podemos reutilizar la lógica de presentación en otras interfaces de usuario, de este modo podremos implementar por ejemplo, el mismo formulario usando formularios Windows, Silverlight o WPF de tal manera que nuestra única preocupación será implementar la interfaz de usuario con la tecnología seleccionada.

La capa de presentación debería ser probada al igual que el resto de capas, una de las ventajas de conseguir desacoplar la lógica de presentación de la interfaz de usuario, es que podemos probarlas por separado.

Aunque no es fácil probar la interfaz de usuario dentro de un entorno de testing, si que podemos mover todo el código testeable correspondiente a los manejadores de eventos a métodos dentro de una clase.

Un arquitecto no debe implicarse directamente en la definición de los detalles de la interfaz de usuario, en su lugar, debe preocuparse de la definición de los datos que se manejaran en cada formulario y  de aspectos de calidad como por ejemplo la usabilidad y la accesibilidad.

Una vez que el arquitecto ha definido con éxito los datos que se manejaran en cada formulario con los usuarios del sistema, el equipo de desarrollo puede proceder a implementar y probar la lógica de presentación al mismo tiempo que los diseñadores gráficos se ocupan de desarrollar la interfaz de usuario.

Y eso es todo por hoy, en el siguiente post hablare sobre el patrón Modelo Vista Presentador, de sus ventajas e implementación.

Posted: Dec 14 2009, 05:56 AM by ccrego | with no comments
La Capa de Servicios AJAX

Cuando trabajamos con una capa de servicios local implementada a través de un assembly y usamos ASP.NET para implementar nuestra capa de presentación, la capa de servicios se encontrara alojada en el mismo worker process de IIS.

Sin embargo si tenemos intención de explotar nuestra capa de servicios usando AJAX o Silverlight, debemos realizar algunos ajustes, especialmente en el área de seguridad.

Una de las características de AJAX y Silverlight es la capacidad para llamar a capas de servicios remotas a través de HTTP, en estos casos la comunicación tiene lugar a través de peticiones HTTP a una dirección y puerto determinados que son expuestos públicamente por el servidor web.

ArquitecturaSinCapaAjax.JPG

Exponer una capa de servicios a un cliente web es definitivamente un agujero de seguridad, en estos casos es necesario una capa que envuelva a los servicios, a esta capa se la conoce como capa de servicios AJAX.

ArquitecturaConCapaAjax.JPG

La capa de servicios AJAX es la puerta al corazón de nuestro sistema, y como tal cualquier acceso debe ser identificado y autorizado.

En un escenario Web tradicional normalmente usamos autenticación por formularios para filtrar el acceso de los usuarios a las diversas secciones que componen nuestra capa de presentación, en estos casos la seguridad en la capa de servicios la podemos implementar fácilmente a través de  :

  • Seguridad en el Acceso a Código (CAS)
  • WCF
  • Características de Seguridad para Servicios Web

En la autenticación por formularios, el usuario envía sus credenciales y en caso de ser validas, se genera una cookie de autenticación.

Mediante la autenticación por formularios, podemos comprobar en la capa de servicios AJAX la presencia de la cookie de autenticación en la petición HTTP para de esta forma proceder a ejecutar la lógica o a denegar el acceso.

Del mismo modo, podríamos cargar un objeto Usuario en Sesión y recuperarlos en la capa de servicios AJAX para determinar si tiene o no permisos para ejecutar cada uno de los servicios expuestos.

ProcesoAutenticaciónArqAjax.JPG

Como podemos deducir, una capa de servicios AJAX es una capa “tonta” que asegura las llamadas mediante la verificación de credenciales.

Para hacer que nuestros servicios sean accesibles desde el navegador debemos hacerlos “scriptables”, es decir, debemos configurarlos para poder ser invocados desde Javascript.

En .NET podemos implementar una capa de servicios de 3 formas :

  1. Mediante un assembly
  2. Mediante WCF
  3. Mediante Servicios Web Tradicionales

En el caso de servicios web tradicionales, debemos hacer una puntualización, y es que desde la versión 2.0 del Framework tenemos “Servicios Web XML para ASP.NET” o como se conocen técnicamente ASP.NET XML Web Service.

Cuando el motor de ejecución de ASP.NET recibe una petición para un recurso .ASMX, se fija en el content-type de la cabecera HTTP :

  • Si es application/json, instancia el manejador HTTP REST Handler que solo entiende de peticiones en JSON.
  • Si es application/soap, instancia el manejador HTTP estándar que solo entiende de peticiones SOAP.

En el caso del manejador HTTP REST Handler, además se comprueba que la clase que implementa el servicio web .ASMX, este decorada con el atributo [ScriptService].

Para implementar una capa de servicios mediante servicios web ASP.NET XML y exponerlos al navegador web, debemos decorar nuestras clases ASP.NET XML WebService con el atributo [ScriptService].

[ScriptService]
public class EmployeeService : IEmployeeService
{
    [WebMethod]
    public Employee GetEmployeeJSON(int employeeId)
    {
        Employee emp = null;

        // ..

        return emp;
    }

    [WebMethod]
    [ScriptMethod]
    public Employee GetEmployeeXML(int employeeId)
    {
        Employee emp = null;

        // ..

        return emp;
    }
}

Como podemos observar en el fragmento de código anterior, tenemos 2 métodos accesibles, la diferencia principal es que uno de ellos está decorado con el atributo [ScriptMethod].

El atributo [ScriptMethod] nos permite indicar que los datos de un método en un servicio web decorado con el atributo [ScriptService] serán serializados usando XML en lugar de JSON y que se habilitará al método para ser invocado a través de una llamada HTTP GET.

Deshabilitar las llamadas HTTP GET es una medida de seguridad que nos ayuda a evitar llamadas a través de injección de scripts ya que cuando se inyecta un tag <script> solo podemos especificar una URL a través de GET.

Si queremos que nuestro servicio web sea expuesto solo para ser llamado a través de Javascript mediante JSON, tendremos que añadir el siguiente fragmento de configuración en el web.config de la aplicación que aloja los servicios web.

<system.web>
    <webServices>
      <protocols>
        <clear/>
      </protocols>
    </webServices>

Para poder invocar nuestros servicios WCF desde Javacript solo necesitamos :

  • Usar el binding webHttpBinding.
  • Usar el comportamiento webScriptBehaviour para habilitar la serialización de datos en JSON y habilitar las llamadas desde Javascript.

Como he explicado antes, los servicios de la capa de servicios AJAX pueden ser llamados por dos tipos de usuarios :

  1. Autenticados
  2. No Autenticados

La diferencia entre estos dos tipos de usuarios es que los usuarios Autenticados tienen un objeto de autenticación que nos permite determinar la identidad del usuario y sus roles de autorización, este objeto lo podemos implementar a través de :

  • La cookie de autenticación generada con autenticación basada en formularios
  • Un objeto de autenticación cargado en Sesión

Partiendo de esta base, los métodos de la capa de servicios AJAX son extremadamente sencillos de implementar, veamos un ejemplo :

public class Role 
{
    public int ID
    { get; set; }

    public string Name
    { get; set; }

    public Role(int id, string name)
    {
        // Assign the parameters ..
    }

    // Role Members ..
}

public static class RoleFactory
{
    public static Role GetAdministratorRole()
    {
        return new Role(1, "Administrator");
    }

    public static Role GetApplicationRole()
    {
        return new Role(2, "ApplicationRole");
    }
}

public interface IUser 
{
    public IDictionary<int, Role> Roles
    { get; set; }
}

public static class UserFactory
{
    public static IUser Load(HttpContext context)
    {
        return new MyUser(context);
    }
}

public class MyUser : IUser
{
    public MyUser(HttpContext context)
    {
        // Map the context.User to MyUser ..
    }

    public IDictionary<int, Role> Roles
    {
        get
        {                
            // Get the roles from database ..
        }
        set
        {
            // Set the roles ..
        }
    }
}
[ScriptService]
public class MyCatalogService : ICatalogService
{
    [ScriptMethod]
    public Article GetArticle(int articleId)
    {
        Article article = null;

        if (HttpContext.Current == null)
        {
            throw new ArgumentNullException("HttpContext");
        }

        IUser user = UserFactory.Load(HttpContext.Current);

        if (user == null)
        {
            throw new ArgumentNullException("IUser from HttpContext");
        }

        if (user.Roles.ContainsKey(RoleFactory.GetUserApplicationRole().ID))
        {
            // Get the article from service layer ..                
        }

        return article;
    }

    // Other members for ICatalogService ..
}

Como podemos observar, el servicio hace uso del contexto HTTP donde tenemos toda la información relativa al usuario autenticado, en este aspecto podemos :

  1. Implementar nuestro propio mecanismo de seguridad como se hace en el ejemplo a través del objeto IPrincipal HttpContext.Current.User, o bien recibir nuestro propio objeto de autenticación en Sesión a través de HttpContext.Current.Session[“loggedUser”].
  2. Usar seguridad basada en objetos IPrincipal nativos de .NET, siempre y cuando utilicemos autenticación integrada y basemos nuestra seguridad en los Roles de Windows o del Directorio Activo.

El uso del objeto HttpContext.Current es soportado de forma nativa por los servicios web ASP.NET XML puesto que se ejecutan en el mismo “worker process” de IIS que la aplicación ASP.NET, sin embargo para usarlos en WCF debemos aplicar un poco de configuración.

Cuando el motor de ejecución de ASP.NET recibe una petición para un recurso .SVC y comienza a procesarla la redirige al motor de ejecución de WCF se apropia de ella y no la devuelve al motor de ejecución de ASP.NET, como resultado el valor del contexto HTTP siempre es nulo.

Para conseguir que ASP.NET y WCF funcionen como es debido, debemos habilitar la compatibilidad ASP.NET en WCF a través del fichero de configuración del servicio WCF :

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>    
    <services>
      <!-- 
        Definicion de servicios ..         
          - Importante usar el enlace webHttpBinding
      -->      
    </services>
    <behaviors>
      <!-- 
        Definicion de comportamientos .. 
          - Importante usar webScriptBehaviour ..
      -->
    </behaviors>
  </system.serviceModel>

Además de habilitar la compatibilidad con ASP.NET en la configuración del servicio WCF, debemos indicarlo de forma explícita en la implementación de los contratos expuestos :

[AspNetCompatibilityRequirements(
    RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyCatalogService : ICatalogService
{
    // Service Members ..
}

Y esto es todo por hoy ..

Posted: Dec 11 2009, 06:15 AM by ccrego | with no comments
Filed under: , ,

SOA : Arquitectura Orientada a Servicios

La arquitectura orientada a servicios es un paradigma de programación cuyo objetivo es ver la funcionalidad de un sistema como una colección de procesos de negocio que son expuestos como servicios altamente interoperables.

Uno de los valores de SOA, es que nos empuja a modelar procesos de negocio débilmente acoplados y altamente interoperables a través de estándares abiertos.

Una solución SOA se compone de servicios que pueden haber sido implementados en distintos lenguajes de programación y estar alojados en diferentes plataformas.

Los principios de SOA son :

  1. Control limitado
  2. Autonomía
  3. Uso de contratos en lugar de clases
  4. Compatibilidad semántica

El principio de “Control limitado” hace referencia a que cualquier interacción con un servicio se realiza a través de la interfaz pública del mismo donde los parámetros de entrada de los métodos representan los mensajes enviados al servicio.

A la interfaz pública de un servicio se la conoce como “Contrato de servicio”.

Es muy importante por tanto diseñar un contrato de servicio guardando toda la sencillez que nos sea posible, esto implica :

  1. No usar objetos con referencias circulares en los parámetros de entrada y en los valores de retorno.
  2. Usar parámetros de entrada y valores de retorno tipados.

El principio de “Autonomía” hace referencia al concepto de servicios débilmente acoplados que puedan diseñarse y ser desplegados independientemente del resto de servicios, pero que a su vez sean capaces de comunicarse con otros a través de contratos y políticas.

En SOA, los servicios se comunican mediante el intercambio de contratos de datos.

Un contrato de datos se traduce en una clase que se compone exclusivamente de propiedades y que viene a representar a un objeto DTO que no tiene nada que ver con los objetos del modelo de dominio.

En resumen, un contrato de datos agrupa los parámetros de entrada de un servicio.

Sin embargo los contratos de servicio y los contratos de datos no son demasiado importantes si el servicio no hace exactamente lo que el consumidor espera de el.

La información relativa a la funcionalidad de un servicio es conocida como “semántica de servicio”, dicha semántica debe ser expuesta y descubierta a través de políticas.

Para expresar políticas a nivel de servicio y poder descubrirlas es necesario usar la especificación WS-Policy.

Construir un sistema basado en SOA, es un proceso incremental que no requiere de una revisión completa de los procesos de negocio existentes.

SOA habla de servicios, pero los servicios no son un tipo concreto de tecnología y usar servicios en una aplicación no implica que nuestra aplicación use SOA.

Lo que nos tiene que quedar claro es que SOA es un conjunto de principios que inspiran buenas prácticas en el diseño de servicios.

Hay mucha gente que tiene por costumbre relacionar SOA con los servicios web, sin embargo los servicios web no tienen nada que ver con SOA :

  1. Una aplicación que use servicios web no es una aplicación SOA
  2. Un servicio web no es SOA si no cumple con los 4 principios que acabo de mencionar.

Hubo un tiempo en que se hablaba mucho sobre SOA y sus ventajas, y entonces todo el mundo se intereso por el tema, sin embargo SOA no es una revolución ya que sus principios fueron escritos hace mas de 15 años.

Nosotros como desarrolladores deberíamos centrarnos en el problema que debemos resolver y pensar en una solución eficaz, en el caso de sistemas distribuidos, para dar forma a nuestra solución debemos identificar los servicios que modelan la solución.

Aplicar los principios SOA a una capa de servicios significa tener contratos de servicios bien definidos que actúen como fachada de la lógica de negocio, para conseguirlo podemos seguir las siguientes reglas prácticas :

  • Los servicios de la capa de servicios no implementan lógica de negocio, en su lugar la exponen y consumen.
  • El intercambio de datos ocurre a través de objetos DTO y no a través de clases del modelo de objetos de dominio.
  • Los servicios no distribuyen objetos ni trabajan sobre ellos, en su lugar se limitan a devolver una colección de valores a través de un formato.
  • Los servicios de la capa de servicios deben estar enfocados hacia una interfaz de usuario.
  • Cualquier servicio con una interfaz basada en objetos tiende a exponer operaciones CRUD, estos servicios son conocidos como CRUDy, y por tanto no son SOA.
  • Aplicar versionado y extensibilidad sin modificar los contratos de datos y de servicio.

Veamos un ejemplo de servicios CRUDy :

public interface IEmployeeService
{
    FindEmployeeByIDResponse FindEmployeeById(FindEmployeeByIDRequest request);

    CreateEmployeeResponse CreateEmployee(CreateEmployeeRequest request);

    UpdateEmployeeResponse UpdateEmployee(UpdateEmployeeRequest request);

    DeleteEmployeeResponse DeleteEmployee(DeleteEmployeeRequest request);
}

Antes de ponernos manos a la obra para implementar un servicio CRUDy, debemos asegurarnos que tenemos casos de uso que utilizan funcionalidad de alta, baja y modificación.

Sin embargo, en un servicio debemos evitar a toda costa tener métodos que sean más específicos que los casos de uso que los consumen.

Imaginemos que para registrar un empleado debemos realizar los siguientes pasos :

  1. Comprobar que el DNI no existe en base de datos
  2. Registrar los datos en base de datos
  3. Obtener el identificador generado

Si encapsulamos toda esta funcionalidad en el método CreateEmployee de nuestro servicio IEmployeeService, estaremos haciendo lo correcto, en caso contrario, si exponemos un método para cada paso, estaremos aplicando el antipatrón CRUDy.

Las interfaces que aplican el antipatrón CRUDy, se conocen como “interfaces chatty”, y se caracterizan por exponer un conjunto de métodos que deben ser llamados en secuencia desde la capa de presentación para cumplir con un determinado caso de uso.

Y esto ha sido todo por hoy (ahora sí, que ya toca cena) …

Posted: Dec 08 2009, 01:19 PM by ccrego | with no comments
Filed under: ,

La Capa de Servicios : Patrones

La lógica de aplicación que se espera de una capa de servicios es esencialmente :

  • Gestión de roles
  • Validación de datos
  • Notificación
  • Adaptación de datos a la interfaz de usuario

El patrón Remote Facade se aplica a través de un conjunto de métodos que modifican la granularidad de las operaciones existentes, no implementa nuevas funcionalidades, en su lugar, crea una fachada sobre una API existente.

El patrón Remote Facade modifica el modo en que se accede a un conjunto de objetos, es decir, si queremos tratar con una versión simplificada de una API, debemos crear una fachada que es precisamente la definición de este patrón.

Imaginemos que tenemos el siguiente contrato en una capa de servicios local :

public interface IEmployeeService
{
    int Create(Employee employee);

    int Update(Employee employee);

    IList<Employee> FindAll();

    Employee FindById(int employeeId);

    Employee GetBoss(int employeeId);

    Department GetDepartment(int employeeId);

    IList<Employee> GetSubordinates(int employeeId);
}

Ahora el mismo contrato, pero preparado para ser expuesto de forma remota con WCF :

[ServiceContract]
public interface
IEmployeeService
{
    [OperationContract]
    int Create(Employee employee);

    [OperationContract]
    int Update(Employee employee);

    [OperationContract]
    IList<Employee> FindAll();

    [OperationContract]
    Employee FindById(int employeeId);

    [OperationContract]
    Employee GetBoss(int employeeId);

    [OperationContract]
    Department GetDepartment(int employeeId);

    [OperationContract]
    IList<Employee> GetSubordinates(int employeeId);
}

Ahora la pregunta es, ¿Cuando necesitamos aplicar el patrón Remote Facade?.

Usaremos el patrón Remote Facade cuando tengamos que refactorizar la capa de servicios a una interfaz más simplificada del contrato IEmployeeService, o cuando tengamos que hacer uso de objetos DTO en la firma de los métodos :

public interface IEmployeeServiceRF
{
    int Create(EmployeeDto employee);

    int Update(EmployeeDto employee);

    IList<EmployeeDto> FindAll();

    EmployeeDto FindById(int employeeId);

    EmployeeDto GetBoss(int employeeId);    

    IList<EmployeeDto> GetSubordinates(int employeeId);
}

La clase EmployeeDto puede ser un subconjunto o un superconjunto del objeto de dominio Employee (puede incluir el objeto Department).

----------------------------------------------------------------------------------------------------------

El patrón DTO se aplica creando objetos que conducen datos a través de las capas de una aplicación, con el objetivo principal de reducir los accesos a base de datos y minimizar el acoplamiento entre la capa de presentación y el modelo de dominio.

Sea cual sea la motivación que nos lleve a usar objetos DTO, debemos tener muy en cuenta que un objeto DTO solo expone propiedades.

Lo que está claro es que no podemos usar objetos nativos del modelo de dominio en todas las capas de servicios, esto solo es posible si la capa de presentación y la capa de servicios están alojadas en la mismo lugar, como por ejemplo en una capa de presentación web.

Si una capa de servicios está ubicada en otra capa es muy normal usar objetos DTO en lugar de objetos nativos del dominio.

Tanto WCF como los servicios web tradicionales no pueden serializar objetos con referencias circulares puesto que están basados en serialización XML y este es uno de los motivos que nos lleva muchas veces a usar objetos DTO en capas de servicios remotas, veamos un ejemplo de referencias circulares en un modelo de dominio :

public class Section
{
    /// <summary>
    /// A section of a shopping center has a manager
    /// </summary>
    public Manager Manager
    {
        get;
        set;
    }

    // Other properties ..
}

public class Manager
{
    /// <summary>
    /// A manager of a shopping center runs a section
    /// </summary>
    public Section Section
    {
        get;
        set;
    }

    // Other properties ..
}

Sin embargo la serialización binaria si soporta referencias circulares.

Se recomienda usar objetos DTO para encapsular los parámetros de entrada y los valores de retorno de los métodos de la capa de servicios.

Tener objetos DTO en la firma de los métodos de una capa de servicios añade una nueva capa de abstracción, y por tanto un nivel más de complejidad.

No es aconsejable usar objetos DTO cuando tenemos que trabajar con un modelo de dominio complejo ya que por cada objeto del dominio podríamos :

  • Tener de 1..N objetos DTO
  • Por cada DTO tener una clase que adapte la intefaz del objeto de dominio

Como nos podemos imaginar en esta situación la refactorización del modelo de dominio se puede convertir en una autentica pesadilla.

Cuando usamos objetos DTO en aplicaciones multicapa en algún momento tenemos que convertir los objetos del modelo de dominio a objetos DTO y es entonces cuando deberíamos hacer uso del patrón Adapter.

----------------------------------------------------------------------------------------------------------

El patrón Adapter convierte la interfaz de una clase en otra y viceversa, a este proceso se le conoce como adaptación de interfaces, sin embargo, como acabo de explicar antes en un modelo de dominio complejo, los objetos DTO y los objetos Adapter añaden sobrecarga.

Veamos un ejemplo de la implementación del patrón Adapter :

public class Employee 
{
    private EmployeeAdapter _adapter;

    public int ID   
    { get;set; }

    public string Name
    { get;set; }

    public string FirstSurname
    { get;set; }

    public string SecondSurname
    { get;set; }

    public bool IsExternal
    { get;set; }

    public string DNI
    { get;set; }

    public DateTime CreationDate
    { get;set; }

    public Employee() { }

    public Employee(EmployeeDTO employeeDTO)
    {
        _adapter = new EmployeeAdapter(employeeDTO);
        _adapter.Initialize(this);
    }
}

public class EmployeeDTO
{
    private EmployeeDTOAdapter _adapter;

    public int ID
    { get; set; }

    public string FullName
    { get; set; }

    public bool IsExternal
    { get; set; }

    public string DNI
    { get; set; }

    public DateTime CreationDate
    { get; set; }

    public EmployeeDTO() { }

    public EmployeeDTO(Employee employee)
    {
        _adapter = new EmployeeDTOAdapter(employee);
        _adapter.Initialize(this);
    }
}

internal class EmployeeDTOAdapter
{
    private Employee _employee;

    public EmployeeDTOAdapter(Employee employee)
    {
        _employee = employee;
    }

    public void Initialize(EmployeeDTO employeeDTO)
    {
        employeeDTO.ID = _employee.ID;
        employeeDTO.DNI = _employee.DNI;            
        employeeDTO.FullName = string.Format("{2} {1}, {0}", _employee.Name, 
                                                                                       _employee.FirstSurname, 
                                                                                       _employee.SecondSurname);            
        employeeDTO.IsExternal = _employee.IsExternal;
        employeeDTO.CreationDate = _employee.CreationDate;
    }
}

internal class EmployeeAdapter
{
    private EmployeeDTO _employeeDTO;

    public EmployeeAdapter(EmployeeDTO employeeDTO)
    {
        _employeeDTO = employeeDTO;
    }

    public void Initialize(Employee employee)
    {
        employee.ID = _employeeDTO.ID;            
        employee.DNI = _employeeDTO.DNI;

        string[] fullNameSplitComma = _employeeDTO.FullName.Split(',');
        employee.Name = fullNameSplitComma[1];

        string[] surNamesSplitSpace = fullNameSplitComma[0].Split(' ');
        employee.FirstSurname = surNamesSplitSpace[1];
        employee.SecondSurname = surNamesSplitSpace[0];

        employee.IsExternal = _employeeDTO.IsExternal;
        employee.CreationDate = _employeeDTO.CreationDate;
    }
}

public class TestAdapters
{
    public void Run()
    {
        Employee employee = new Employee()
        {
            ID = 1,
            Name = "Carlos",
            FirstSurname = "Crego",
            SecondSurname = "Sanchez",
            DNI = "52497274G",
            IsExternal = true,
            CreationDate = new DateTime(1981, 7, 16)
        };

        EmployeeDTO employeeDto = new EmployeeDTO(employee);

        Employee empAdapted = new Employee(employeeDto);
    }
}

Como podemos observar en el ejemplo, para tener una adaptación bidireccional, debemos construir dos clases “Adapter” :

  1. Una clase Adapter para convertir un objeto del modelo de dominio en un objeto DTO
  2. Una clase Adapter para convertir un objeto DTO en un objeto del modelo de dominio.

Terminare de hablar del patrón Adapter con el siguiente trozo de código que seguro que os será muy familiar si alguna vez os habeis pegado con WCF :

public static class EmployeeRepository 
{
    public static Employee GetById(int employeeId)
    {
        Employee emp = null;

        // Gets the employee from the database ..

        return emp;
    }
}

public class EmployeeServiceFindByIdRequest
{
    public int ID   
    { get;set; }
}

public class EmployeeServiceFindByIdResponse 
{
    public int ID   
    { get;set; }

    public string Name
    { get;set; }

    public string FirstSurname
    { get;set; }

    public string SecondSurname
    { get;set; }

    public bool IsExternal
    { get;set; }

    public string DNI
    { get;set; }

    public DateTime CreationDate
    { get;set; }
}

public class EmployeeServiceFindByIdAdapter
{
    private Employee _employee;

    public EmployeeServiceFindByIdAdapter(Employee employee)
    {
        _employee = employee;
    }

    public void Initialize(EmployeeServiceFindByIdResponse response)
    {
        response.ID = _employee.ID;
        
        // The Others assignations .. 
    }
}

public class EmployeeService : IEmployeeService
{
    public EmployeeServiceFindByIdResponse FindById(EmployeeServiceFindByIdRequest request)
    {
        Employee employee = EmployeeRepository.GetById(request.ID);

        EmployeeServiceFindByIdAdapter adapter 
            = new EmployeeServiceFindByIdAdapter(employee);

        EmployeeServiceFindByIdResponse response 
            = new EmployeeServiceFindByIdResponse();

        adapter.Initialize(response);

        return response;
    }

    // Other IEmployeeService methods ..
}

----------------------------------------------------------------------------------------------------------

Como he explicado ya, en un modelo de dominio complejo, es poco práctico hacer uso de objetos DTO y objetos Adapter, pero entonces, ¿Cual es la solución práctica?.

Muy sencillo, usar directamente objetos de modelo de dominio.

Pero entonces, ¿Cuando podemos prescindir de objetos DTO?

Solo podremos eliminar la presencia de objetos DTO cuando la capa de presentación y la capa de servicios estén ubicadas en el mismo proceso, un claro ejemplo son los escenarios .NET to .NET donde los tipos del CLR se pueden entender en ambas capas.

Algunos escenarios donde podemos eliminar la presencia de objetos DTO son :

  • Una capa de presentación en ASP.NET y una capa de servicios implementada a través de un assembly.
  • Una capa de presentación en ASP.NET y  una capa de servicios implementada a través de servicios web ASMX o servicios WCF siempre y cuando ámbas capas estén alojadas en el mismo worker process de IIS.

Sin embargo, usar objetos nativos de dominio disminuye la granularidad de la solución puesto que en muchos casos contienen más información de la que realmente necesitamos, y es entonces cuando nos planteamos la posibilidad de crear varias versiones del mismo objeto de dominio :

public enum EmployeeVersion
{
    FullVersion = 0,
    ShortVersion,
    IdVersion
}

public class Employee 
{
    public int ID   
    { get;set; }

    public string Name
    { get;set; }

    public string FirstSurname
    { get;set; }

    public string SecondSurname
    { get;set; }

    public bool IsExternal
    { get;set; }

    public string DNI
    { get;set; }

    public DateTime CreationDate
    { get;set; }

    public EmployeeVersion Version
    { get; set; }    
}

En el ejemplo anterior, a través de la propiedad “EmployeeVersion”, podremos implementar una capa de presentación inteligente que conozca los campos “no nulos” en función del tipo de versión del objeto de dominio.

Y eso es todo por hoy, en el siguiente post hablare sobre arquitectura SOA.

Posted: Dec 08 2009, 07:39 AM by ccrego | with no comments
La Capa de Servicios : Aspectos de Implementación

Las clases usadas en la capa de servicios deberían exponer un contrato :

  • A través de WCF (para capas de servicios remotas)
  • A través de una interfaz (para capas de servicios locales)

Normalmente en una capa de servicios local, la interfaz usada para exponer el contrato hace uso de objetos DTO para el paso de parámetros y el retorno de datos.

Para preparar la lista de métodos de la interfaz usada para exponer un contrato, es muy normal basarse en los casos de uso del sistema.

En la mayoría de las ocasiones, acabaremos teniendo un clase de servicio por cada objeto del dominio :

  • CustomerService
  • OrderService
  • EmployeeService

Sin embargo si tenemos pocas acciones y todas están estrechamente relacionadas con una sola clase de servicio bastaría.

Una capa de servicios evoluciona de modo independiente del resto del sistema y es la única interfaz para que la capa de presentación haga uso de los procesos de negocio.

El principal problema de una capa de servicios es que si un caso de uso cambia, tendremos que modificar el servicio correspondiente, el beneficio que ganamos a cambio es que muchas veces no tendremos que modificar la capa de negocio.

Es muy recomendable que cada clase de la capa de servicios exponga una interfaz :

public interface IEmployeeService
{
    int Create(Employee employee);

    int Update(Employee employee);

    IList<Employee> FindAll();

    Employee FindById(int employeeId);

    Employee GetBoss(int employeeId);

    Department GetDepartment(int employeeId);

    IList<Employee> GetSubordinates(int employeeId);
}

public class MyEmployeeService : IEmployeeService
{
    #region IEmployeeService Members

    public int Create(Employee employee)
    {
        // Create a new employee ..
    }

    public int Update(Employee employee)
    {
        // Updates the employee ..
    }

    public IList<Employee> FindAll()
    {
        // Find all employees ..
    }

    public Employee FindById(int employeeId)
    {
        // Gets the employee object for the employee identificator ..
    }

    public Employee GetBoss(int employeeId)
    {
        // Gets the employee object that represents the boss for 
        // the employee identificator ..
    }

    public Department GetDepartment(int employeeId)
    {
        // Gets the Department object associated to the 
        // employee identificator ..
    }

    public IList<Employee> GetSubordinates(int employeeId)
    {
        // Gets the subordinates for the employee identificator ..
    }

    #endregion
}

Imaginemos que la interfaz IEmployeeService usa los objetos del modelo de dominio directamente, en este caso la clase Employee es exactamente una representación del objeto Employee que hemos creado en el modelo de dominio, en este caso estamos asumiendo el uso del patrón modelo de dominio en la capa de negocio.

La mayoría de los expertos en arquitectura de software recomiendan que los métodos CRUD no deberían aparecer en los servicios sin embargo depende de los casos de uso que tengamos, es decir, si la funcionalidad relativa a persistir información forma parte de un caso de uso, entonces dicha funcionalidad debe formar parte del servicio.

Pasemos a otro tema, ¿Donde deberíamos aplicar el manejo de la seguridad?.

Es decir, ¿Que debería devolver el método FindAll? :

  • Todos los empleados
  • Todos los empleados visibles para el usuario actual

Si nos tomamos en serio la seguridad, deberíamos elegir entre :

  • Comprobar la identidad del usuario que realiza la llamada al servicio y rechazar las llamadas de usuarios no autorizados.
  • Asumir que el usuario que realiza la llamada solo tiene acceso a una parte de los datos basándose por ejemplo en los roles a los que pertenece.

La verificación de permisos y la configuración de la respuesta es una responsabilidad exclusivamente de los métodos de la capa de servicios, si usamos roles, el servicio debe obtener la identidad del usuario que realiza la llamada, obtener la lista de roles a los que pertenece y decidir que hacer.

Resumiendo, la funcionalidad relativa a la restricción de datos en función del perfil del usuario que realiza la llamada a la capa de servicios, forma parte de la definición de un caso de uso y por tanto de las reglas de negocio.

Para terminar hablare de la diferencia entre “lógica de dominio” y “lógica de aplicación” :

La lógica de dominio expresa conceptos de negocio como reglas, persistencia y estado, la lógica de dominio suele ser implementada a través de objetos que ignoran la persistencia pero cuyos datos y comportamiento es consistente en relación a las reglas.

La lógica de aplicación organiza los objetos del dominio y los servicios de aplicación para servir a la capa de presentación lo que se necesita.

Y eso es todo por hoy, en el siguiente post hablare de los patrones que se suelen usar en la implementación de la capa de servicios ..

Posted: Dec 07 2009, 04:23 PM by ccrego | with no comments
Filed under:

La Capa de Servicios : Conceptos Básicos

Tener cierta cantidad de lógica de negocio en la capa de presentación puede ser aceptable algunas veces, sin embargo, consideremos que guardamos toda esa lógica de negocio fuera de la capa de presentación para tener lo que se considera una vista tonta (view dummy), y débilmente acoplada.

¿Como podríamos acceder a dicha lógica de negocio desde la capa de presentación?.

La respuesta es sencilla, definiendo una capa de servicios. 

Una capa de servicios es una capa de abstracción que se crea entre la capa de presentación y la capa de negocio, y que agrupa funcionalidad de la capa de negocio para ser expuesta a la capa de presentación.

La funcionalidad de una capa de servicios no se debe limitar a llamar a métodos de la capa de negocio ya que actuaria de proxy, por el contrario, una capa de servicios debe organizar las llamadas a un conjunto de clases de negocio para ofrecer una funcionalidad muy concreta a la capa de presentación.

Cualquier interacción que se origine en la capa de presentación, encuentra una respuesta en la capa de servicios.

¿Es la capa de servicios entonces, la única parte del sistema que realiza llamadas directamente a la base de datos?, la respuesta es no.

La lógica de negocio puede contener componentes que no tienen acceso a base de datos, y que manejan datos de otras fuentes a través de servicios web y ficheros.

Es muy común que la capa de presentación se comunique con la capa de servicios mediante objetos DTO que internamente son convertidos a objetos del modelo de dominio en la capa de servicios.

ArquitecturaServicios2[1]

Como he comentado antes, un servicio agrupa llamadas a clases de negocio para ofrecer una funcionalidad muy concreta, en este escenario es inevitable pensar que los servicios sean un tipo especial de clase que tome ventaja de algunos aspectos del entorno de ejecución como son:

  • Seguridad
  • Transacciones
  • Instrumentación
  • Fiabilidad
  • Políticas

Por tanto, el entorno de ejecución marca la diferencia entre un servicio y una clase.

arquitecturaservicios[1]

Como se puede observar en la figura anterior, una capa de servicios puede implementarse de dos formas diferentes :

  1. Remota a la capa de presentación
  2. Local a la capa de presentación

Cuando debemos optar por una capa remota?, pues depende del cliente y de los recursos que tengamos a mano.

En la mayoría de ocasiones que implementamos una capa de servicios de forma remota mediante servicios WCF, es debido a que trabajamos en un entorno web.

Mediante el uso de una capa de servicios remota podemos realizar el despliegue en un servidor web dedicado, permitiendo aliviar la carga de trabaja al servidor web donde tenemos alojada nuestra aplicación ASP.NET.

Además tener la capa de servicios externalizada es perfecto para escenarios SOA.

Para terminar, una capa de servicios esta compuesta de :

  • Macro-servicios
  • Micro-servicios

Los macro-servicios, viven en la capa de servicios y orquestan operaciones que se mapean directamente en casos de uso, son servicios que no entienden de componentes de negocio, solo de flujos de llamadas que dan sentido al caso de uso que representan.

Los micro-servicios, viven en la capa de presentación y representan “utilidades” que sirven a la lógica de negocio, un claro ejemplo de estos servicios son las clases de formateo de datos, de conversión y de acceso a ficheros de datos.

Y eso ha sido todo por hoy ..

Posted: Dec 05 2009, 01:11 PM by ccrego | with no comments
Filed under:

La Capa de Negocio (VI) : El Patrón Modelo de Dominio

En un diseño orientado al modelo de datos, comenzamos por diseñar la base de datos para después crear los componentes de negocio.

Cuando la complejidad del sistema crece, nuestro enfoque en los datos deja de tener importancia ya que debemos centrarnos en la funcionalidad y es entonces cuando un diseño orientado a datos deja de ser escalable.

El patrón modelo de dominio nos permite centrarnos en el comportamiento del sistema y en el flujo de datos que lo hace funcionar, además nos ayuda a obtener un modelo de objetos que es una representación del modelo conceptual del sistema.

El modelo de dominio describe las entidades que participan en el sistema, las relaciones y el flujo de datos que existe entre ellas, dichas entidades se mapean en clases que se componen de propiedades y métodos.

Un modelo de dominio es independiente de la base de datos y por tanto podemos reutilizarlo en otras aplicaciones.

Sin embargo el patrón modelo de dominio tiene un alto coste inicial, aunque a cambio ganamos un comportamiento lineal con respecto al aumento de la complejidad.

A diferencia de un diseño orientado al modelo datos, el diseño orientado al dominio es dirigido por los requerimientos y nos obliga a entender las reglas que definen el negocio y a modelarla a través de clases.

El único inconveniente es que es muy complicado tener una visión completa del sistema en términos de entidades y relaciones que describen procesos, además, en algún momento debemos convertir nuestro modelo de dominio en tablas y es entonces cuando nos damos cuenta de lo duro que resulta la persistencia de nuestro modelo.

Para realizar la persistencia de nuestro modelo de dominio podemos apoyarnos en alguna herramienta ORM como NHibernate o Entity Framework.

En un escenario recurrente para objetos de dominio, podemos definir clases base que contienen el comportamiento recurrente que deseamos compartir, imaginemos que deseamos aplicar validación sobre nuestros objetos de dominio, podríamos utilizar el siguiente modelo :

public interface IValidable
{
bool IsValid { get; }

ValidationResults Validate();
}

public class BussinesObject :
IValidable
{
public bool IsValid
{
get
{
return Validate().IsValid;
}
}

public ValidationResults Validate()
{
Validator validator = ValidationFactory.CreateValidator(this.GetType());

ValidationResults results = new ValidationResults();

validator.Validate(this, results);

return results;
}
}

public class Order :
BussinesObject
{
private int _id;
private string _name;
private string _type;

private IList<Product> _products;

[NotNullValidator]
[StringLengthValidator(8,8)]
public int ID
{
get { return _id; }
set { _id = value; }
}

[NotNullValidator]
[StringLengthValidator(255)]
public string Name
{
get { return _name; }
set { _name = value; }
}

[StringLengthValidator(4,4)]
public string Type
{
get { return _type; }
set { _type = value; }
}

public void AddProduct(Product product)
{
// ...
}

public void RemoveProduct(int id)
{
// ..
}
}

public class
OrderRepository
{
public Order Recover(int id)
{
Order order = null;

// Gets the fields from database ..

// Apply the mapping ..

return order;
}

public void Create(Order order)
{
if (order.IsValid)
{
throw new ValidationException("The order is not valid.");
}

// Insert the order in the 'Order' table ..
}

public void Update(Order order)
{
if (order.IsValid)
{
throw new ValidationException("The order is not valid.");
}

// Update the order in the 'Order' table ..
}
}

Los objetos del modelo de dominio no deben contener código para cargar el estado de base de datos ni tampoco para guardarlo, dicho código debe declararse en otras clases de la capa de negocio, una buena práctica para unificar este código es usar el patrón repositorio.

El patrón repositorio consiste en crear una capa de abstracción que actúe de puente entre la capa de datos y la capa de negocio.

Las clases repositorio, deben poder invocarse desde la capa de presentación, y normalmente tenemos una por cada objeto.

La separación de las clases de negocio de la persistencia nos permite identificar los métodos de acceso a base de datos para posteriormente crear una interfaz que podremos usar desde una “fábrica de repositorios”, de esta forma podemos reutilizar el modelo de dominio con otros proveedores de base de datos.

public class IOrderRepository
{
public Order Recover(int id);

public void Create(Order order);

public void Update(Order order);
}

Dentro de los métodos de las clases “repositorio”, tenemos llamadas a la capa de datos puesto que en algún momento necesitaremos ejecutar un comando de base de datos, cuando utilizamos el patrón modelo de dominio las clases que realizan las operaciones de conversión de entidades a tablas son las clases que implementan el patrón “Data Mapper”.

Una clase “Data Mapper” conoce las columnas de tablas de base de datos necesarias para recuperar el estado de una entidad de negocio.

Ahora bien, ¿Qué ocurre si no encontramos el estado de una entidad en base de datos?, básicamente tenemos 3 opciones :

  1. Lanzar una excepción
  2. Devolver null
  3. Utilizar el patrón Special Case

El patrón Special Case nos recomienda crear tipos específicos para indicar un fallo durante la recuperación del estado de una entidad de negocio de base de datos sin romper el polimorfismo.

La siguiente clase podemos usarla en el método Recover de la clase OrderRepository para indicar la ausencia de estado de entidades Order en base de datos :

public class MissingOrder : Order
{
// ..
}

public class
OrderGateway
{
public Order Recover(int id)
{
Order order = null;

bool notFound = false;

// Gets the notFound value from Database ..

if (notFound)
{
order = new MissingOrder();
}
else
{
// Gets the fields from database ..

// Apply the mapping ..
}

return order;
}

..
Y eso ha sido todo por hoy ..
Posted: Dec 03 2009, 05:20 PM by ccrego | with no comments
La Capa de Negocio (V) : El Patrón Active Record

Los patrones “Table Module” y “Transaction Script”, son patrones procedimentales cuyo foco de implementación está en el modelo de datos en lugar de en casos de uso, sin embargo el patrón “Table Module” está basado en objetos que representan tablas no en objetos de negocio puros.

Al conjunto de objetos y cálculos se le conoce como lógica de negocio y está basada en interacciones entre objetos, por tanto para modelar la lógica se requiere de una visión orientada a objetos y no a datos.

El patrón “Active Record” es una buena elección cuando se requiere de una visión orientada a objetos para implementar una lógica de negocio no muy compleja.

Un objeto “Active Record”, es un objeto que representa un registro de una tabla en base de datos y que se compone de propiedades (que representan las columnas) y métodos.

Por ejemplo, si tenemos un objeto “Active Record” representando un registro de la tabla Clientes, tendremos propiedades como el identificador, el nombre, el cif y el tipo de cliente.

Los componentes más comunes en toda clase “Active Record” son los siguientes :

  • Propiedades representando las columnas del registro que se está representando.
  • Métodos de instancia que representan la lógica aplicada al registro representado.
  • Métodos estáticos que actúan sobre cualquier registro.

El patrón Active Record funciona bien en escenarios donde la lógica de negocio no es muy compleja y donde además no se requiere de capas de abstracción adicionales sobre el modelo de datos.

Desarrollar y mantener una clase “Active Record” por cada tabla puede llegar a ser un autentico infierno si no disponemos de una herramienta ORM que nos de soporte durante la fase de desarrollo y mantenimiento, menos mal que en el mercado tenemos multitud de herramientas de este tipo :

  • LINQ To SQL
  • Entity Framework
  • NHibernate
  • Castle Active Record

Cuando implementamos el patrón “Active Record”, es necesario de una capa adicional que nos permita convertir registros de base de datos a instancias de clase “Active Record”, a esta capa adicional se la conoce como “Capa de mapeo de datos”, aunque los desarrolladores la conocemos mejor por “Mappers”.

La capa de mapeo de datos nos permite desacoplar nuestra clase “Active Record” de la tabla de base de datos correspondiente, puesto que cualquier cambio en el esquema de la tabla, puede implicar modificar la clase “Active Record” correspondiente, aunque hay cambios donde no nos quedara más remedio.

Otro de los puntos débiles de “Active Record” está en el desarrollo de operaciones donde tengamos que trabajar con miles de registros, ya que no nos quedará más remedio que trabajar con miles de instancias de objetos “Active Record” en casos de operaciones de carga o actualización masiva de datos es mejor usar tecnología ADO.NET.

Veamos un ejemplo de clase “Active Record” :

public class Company
{
    private string cif;
    private string nationality;
    private string name;
    private string sector;
    private CompanyType type;

    public string CIF
    {
        get { return cif; }
        set { cif = value; }
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public CompanyType Type
    {
        get { return type; }
        set { type = value; }
    }

    public string Sector
    {
        get { return sector; }
        set { sector = value; }
    }

    public string Nationality
    {
        get { return nationality; }
        set { nationality = value; }
    }

    public Company() { }

    public Company(string cif)
    {

    }

    public int Insert()
    {
        int newId = 0;

        // Insert the company record in the Company table ..

        return newId;
    }

    public void Delete()
    {
        // Delete the company record in the Company table ..
    }

    public void Update()
    {
        // Update the company record in the Company table ..
    }

    public static int GetCount()
    {
        int count = 0;

        // Gets the total of Companies in the Company table ..

        return count;
    }

    public static IList<Company> LoadAll()
    {
        IList<Company> lst = new List<Company>();

        // Gets all the Companies in the Company table ..
        // For many records, use DataTable with ADO.NET ..

        return lst;
    }
}

Otro de los aspectos a tener en cuenta cuando diseñamos un objeto “Active Record” es que las tablas de base de datos contienen claves ajenas que referencian a otras tablas, en este aspecto “Active Record” no es distinto y por tanto debemos tomar la decisión de referenciar a otros objetos “Active Record” a través de un identificador o de un objeto puro :

public class Company
{
    private string cif;
    private int sectorId;

    public int IDSector
    {
        get { return sectorId; }
        set { sectorId = value; }
    }

    ...
public class Company
{
    private string cif;
    private Sector sector;

    public Sector Sector
    {
        get { return sector; }
        set { sector = value; }
    }

    ...

Aunque para gustos los colores, es muy recomendable guardar una estructura lo más cercana a la tabla de base de datos que estamos representando, por tanto es preferible utilizar identificadores en lugar de objetos para referenciar otros objetos Active Record.

Otro aspecto importante es el de conversiones de datos puesto que cuando desarrollamos una clase “Active Record” debemos tomar la decisión de traernos los datos en formato original, o aplicar una conversión por ejemplo a un enumerado.

En general la conversión de tipos está bien cuando por ejemplo usando datos Nullables :

public Nullable<int> IDSector
{
    get { return sectorId; }
    set{ sectorId = value; }
}

public Nullable<DateTime> LastModificationDate
{
    get { return lastModificationDate; }
    set{ lastModificationDate = value; }
}

Sin embargo, no es aconsejable agrupar propiedades en un nuevo objeto ya que estaremos añadiendo un nuevo nivel de abstracción al escribir un código más elaborado y que para nada representa fielmente la estructura del registro de la tabla de base de datos correspondiente :

public class Company
{
    private string cif;
    private string street;
    private string streetType;
    private string country;
    private string zipCode;

    public string CIF
    {
      get { return cif; }
      set { cif = value; }
    }

    public string Street
    {
      get { return street; }
      set { street = value; }
    }

    public string StreetType
    {
      get { return streetType; }
      set { streetType = value; }
    }

    public string ZipCode
    {
      get { return zipCode; }
      set { zipCode = value; }
    }
        private string city;

    public string City
    {
      get { return city; }
      set { city = value; }
    }
    
    ...
public class Address 
{ 
    private string street;
    private string streetType;
    private string city;
    private string zipCode;

    public string Street
    {
      get { return street; }
      set { street = value; }
    }

    public string StreetType
    {
      get { return streetType; }
      set { streetType = value; }
    }

    public string ZipCode
    {
      get { return zipCode; }
      set { zipCode = value; }
    }

    public string City
    {
      get { return city; }
      set { city = value; }
    }

    ...

public class Company
{
    private string cif;
    private Address address;

    public string CIF
    {
      get { return cif; }
      set { cif = value; }
    }

    public Address Address
    {
      get { return address; }
      set { address = value; }
    }

    ..

Cuando agrupamos propiedades en nuevos objetos, nos estamos moviendo hacia una implementación más propia del patrón “Modelo de dominio” del cual hablare en el siguiente post.

Para terminar me gustaría hablar sobre el framework “Castle Active Record” ya que se trata de una implementación Open Source del patrón “Active Record” para la plataforma .NET.

Castle Active Record esta construido sobre NHibernate y por tanto su funcionalidad está basada en la configuración de un fichero Xml que contiene las relaciones entre tablas, sin embargo nos abstrae de muchos detalles que no necesitamos de NHibernate para implementar el patrón “Active Record”.

Más adelante dedicaré un post para cubrir la funcionalidad que nos ofrece este framework.

Y eso ha sido todo por hoy ..

Posted: Dec 02 2009, 07:16 AM by ccrego | with no comments
La Capa de Negocio (IV) : El Patrón Table Module

En escenarios de negocio sencillos es muy probable que podamos convertir cada tabla de base de datos en un componente de negocio.

En la aplicación del patrón “Transaction Script” es muy común usar objetos de tipo DTO para el intercambio de datos, en estos casos lo más normal es que cada propiedad del objeto DTO haga referencia a una columna en base de datos.

El patrón “Table Module” consiste en el mapeo de tablas de base de datos a componentes de negocio que contienen las operaciones necesarias para operar con los datos.

Los datos con los que trabaja una clase “Table Module” no deben alcanzar nunca el nivel de fila de datos, en su lugar, se debe trabajar a nivel de tabla y especificar una clave o índice para referenciar una fila en particular.

Seguramente os preguntareis como se realiza el intercambio de datos entre la capa de presentación y la capa de negocio, la respuesta es muy simple, a través de una representación tabular de los datos.

Cuando un sistema utiliza estructuras tabulares de datos en la capa de presentación y en la capa de datos, el patrón “Table Module” funciona a la perfección puesto que la capa de negocio puede intercambiar datos con la capa de datos de forma directa.

PatronTableModule[1]

El patrón “Table Module” no es más complejo que el patrón “Transaction Script”, pero implementarlo con un modelo de datos complejo puede resultar muy laborioso, menos mal que en Visual Studio.NET disponemos de asistentes para crear nuestros orígenes de datos como estructuras de tipo DataSet.

Mediante el uso del diseñador de objetos Dataset, podemos añadir tablas desde la base de datos y definir las relaciones y restricciones de nuestro modelo.

DataSetDesigner[1]

El patrón “Table Module” está orientado a base de datos y no es adecuado cuando tenemos que trabajar con modelos de datos complejos.

La mayor ventaja que tenemos en el uso del patrón “Table Module” está precisamente en el soporte que nos ofrece Visual Studio .NET, sin embargo, también es una desventaja puesto que a pesar de que tenemos un asistente y entorno de diseño que nos facilitan la vida, el código generado es una autentica caja negra.

En general se podría decir que aplicar el patrón “Table Module” es preferible a aplicar el patrón “Transaction Script” cuando tenemos que trabajar con Visual Studio .NET.

Veamos un ejemplo de código para una clase “Table Module” :

public class AddressTableModule
{
    private DataTable _dataTable;

    public DataTable Addresses
    {
        get { return _dataTable; }
    }
    
    public AddressTableModule(DataTable dataTable)
    {
        _dataTable = dataTable;
    }

    public DataRow GetDataRow(int index)
    {
        return _dataTable.Rows[index];
    }

    public DataRow GetRowByID(int addressId)
    {
        return _dataTable.Rows.Find(addressId);
    }

    public int Insert(int id, string name, int affiliateId, string address, 
                      string city, string codeProvince, string zip, string phone, 
                      string contactPerson, DateTime createdDate)
    {
        // ...

        return 1;
    }

    public int Update(int addressId, DataRow dataRow)
    {
        // ...

        return 1;
    }

    public int Delete(int addressId)
    {
        // ..

        return 1;
    }
}

Como podemos observar, una clase “Table Module” tiene una variable interna que contiene los datos en formato tabular, dichos datos están en memoria y son filtrados y manipulados por varios métodos.

Además la clase “Table Module” hace referencia internamente a una colección de filas de datos en lugar de una colección de objetos de tipo Address.

El problema del código anterior, es que los datos son débilmente tipados y por tanto no tenemos control sobre el contenido real, sin embargo podemos usar objetos TableAdapters en lugar de objetos DataTable para conseguir que nuestras clases “Table Module” sean fuertemente tipadas.

Los objetos TableAdapter fueron introducidos en la versión 2.0 del Framework para trabajar de forma fuertemente tipada con objetos DataTable.

AddressTableAdapter adapter = new AddressTableAdapter();

// DataModel is a typed DataSet with a DataTable with the name 'Address' ..
DataModel.AddressDataTable dataTable = dataTable = adapter.GetData();

Hemos estado viendo como aplicar el patrón “Table Module” sobre estructuras de datos tabulares pero nos falta la funcionalidad necesaria para llenar estas estructuras con datos, para desplegar dicha funcionalidad usamos el patrón “Table Data Gateway”.

Veamos un ejemplo :

public static class AddressGateway
{
    public static DataTable LoadByEmployee(int employeeId)
    {
        DataTable dataTable = new DataTable();

        // ..

        return dataTable;
    }
}
// Gets the tabular structure ..
DataTable dataTable = AddressGateway.LoadByEmployee(1);

// Gets the table module object ..
AddressTableModule addressTM = new AddressTableModule(dataTable);

// Uses the table module object behaviour ..
DataRow dataRow = addressTM.GetRowByID(364);
Y eso ha sido todo por hoy ..
Posted: Dec 01 2009, 05:59 PM by ccrego | with no comments
More Posts Next page »