Frameworks javascript para el Patrón MVVM (Model View ViewModel)

Patron MVVM

El patrón MVVM, nos permite separar la lógica de negocio, el comportamiento de la aplicación y la interfaz de usuario. Lo cual posibilita una separación clara entre desarrollo y diseño. Facilita el desarrollo de aplicaciones web complejas. Se compone de:

  • Model: Modelo de negocio.
  • View: Interfaz de Usuario.
  • ViewModel: Modelo que conecta los ambos anteriores; que contiene el comportamiento de la aplicación.
    • Gestiona la presentación del modelo en los elementos de la Vista.
    • Gestiona las operaciones en la Vista; que afectan al modelo.

Frameworks

Existen multiples Frameworks de Javascript para el Patron MVVM; vamos a comentar tres de ellos:

Knockoutlogo

  • Como se desglosan los componentes:
    • Modelo: Se registran las propiedades del objeto para notificar/recibir cambios de los mismos. Estas propiedades se definen en el modelo como ko.observable
      function Model() {
          var self = this;
          self.name = ko.observable('car');
          self.description = ko.observable('red and offroad');
      }
      
    • View: Se mapean los elementos del html a las propiedades del objeto del modelo.
      </pre>
      <div>Name: <strong data-bind="text: name"></strong></div>
      <div>Description: <strong data-bind="text: description "></strong></div>
      <pre>
    • ViewModel: Contecta ambos:
      ko.applyBindings(new Model());
      
  • Simplicidad y inicio rápido.
  • Mejor rendimiento.
  • Mejor Documentación. Excelente Manual interactivo.

Angularangular

  • Como se desglosan los componentes:
    • Modelo: se define el modelo, no es necesario registrar observables. Automáticamente detecta los cambios en los valores; para proceder a la sincronización.
      function ModelCtrl($scope) {
        $scope.name = 'car';
        $scope.description = 'red and offroad';
      }
      
    • View: Se mapean los elementos mediante {{
      </pre>
      <div>Name: <strong>{{name}}</strong></div>
      <div>Description: <strong>{{description}}</strong></div>
      <pre>
    • ViewModel: Conecta ambos, se indica mediante el tag ng-app; esto permite utilizar el scope automáticamente
      ...
      
      
  • Es mas complejo.
  • Más tendencia de uso.
  • No se limita a registrar (bindear) los objectos, sino también ofrece una solución más completa:
    • Un framework que permite el ciclo complento del modelo (un controlador que permite realizar llamadas ajax al servidor automaticamente).
    • Un framework que permite validación del modelo.
    • Un framework que permite encapsular Vistas parciales (realizando componentes reutilizables).
    • Orientado a IOC y TDD.

Emberember

  • Como se desglosan los componentes:
    • Modelo: se define el modelo “heredando” de la clase Ember.Object.extend.
      App.Model= Ember.Object.extend({
        name: 'car',
        description: 'red and offroad'
      });
      
    • View: Se mapean los elementos mediante {{
      </pre>
      <div>Name: <strong>{{name}}</strong></div>
      <div>Description: <strong>{{description}}</strong></div>
      <pre>
    • ViewModel: Realmente es un framework MVC, el proceso en que las vistas que afectan al modelo se han de programar.
      window.App = Ember.Application.create();
      
Publicado en Desarrollo, javascript, Patron Software | Etiquetado | 2 comentarios

Patrón Promises

En javascript solemos realizar programación asíncrona, con el objetivo de no bloquear el trabajo del usuario (“aplicaciones responsivas”).

Problema

A media que el código crece en complejidad, la situación habitual es realizar un serie de cadenas de callbacks. Nos vemos obligados a crear una función que encadena callbacks o crear callbacks anidados.

// callback encadenados.
function done1() { alert('done1');};
function done2() { alert('done2');};
$.ajax({ url: "/callService"
   	     sucess: function() {done1(); done2(););

// callback Andidados
function done1(done2) {
	alert('done1');
	done2();
}		 	 
$.ajax({ url: "/callService"
   	     sucess: function() { done1(done2); );

Patrón Promise al rescate

Aquí viene a nuestra ayuda el patrón Promises. Nos ofrece una manera más simple de realizar los callbacks.
Promise nos devuelve una tarea, que nos posibilita encadenar los “callbacks” limpiamente.

$.when(tarea).then(done1).then(done2);

Se estructura de la siguiente manera:

  • when: Tarea/s que se realica/en.
  • then: Aquí ponemos los métodos de callback que queremos que se ejecuten al finalizar las tareas lanzadas. Podemos tener un callback de tarea acabada exitosamente o de error.

jQuery (como otras librerias de javascript), incorporan dicho patrón. Bajo el objecto Deferred. Para crear una tarea, retornamos un objecto Deferred. jQuery Ajax ya encapsula por defecto el objecto Deferred.

function promise() {
	var xhr = $.ajax({ url: "/callService");
	return xhr;
}
$.when(promise()).then(done1);

De todas maneras podemos crearnos el objecto Deferred y utilizarlo de manera más avanzada.

function promise() {
	var deferred = $.Deferred();
	var xhr = $.ajax({ url: "/callService",
				   success: function() { 
						...
						deferred.resolve();   //Provoca que se ejecute el callback existoso del then
						}, 
				   error: function() {
						...
						deferred.reject();    //Provoca que se ejecute el callback de error del then
				   });
	return deferred;	
}			   
$.when(promise()).then(done);

Conclusión

Este patrón simplifica enormemente la legibilidad y mantenimibilidad del código. Pero este patrón también se puede aplicar a C#. En el siguiente artículo se ve como Promise Pattern en C#

Publicado en Desarrollo, javascript, web | Dejar un comentario

Trabajo avanzado con ficheros de recursos (.resx)

Me encontré con la necesidad ubicar los recursos en una estructura independiente, no limitándonos a los directorios prefijados en Mvc. Además que los cambios en ellos surtieran efecto sin necesidad de recompilar.

Recursos incrustados vs Contenidos

  • Recurso incrustado: Es la opción por defecto en los proyectos. Los recursos se compilan en dlls; y se cargan en memoria al cargar la dll. Esto es beneficioso para el rendimiento y mantenimiento del código (las claves de recursos se generan automáticamente como constantes). Hay un montón de documentación, yo recomendaría este artículo: Resource Files and ASP.NET MVC Projects
  • Recurso como Contenido: Cuando no queremos volver a compilar el código para cambiar los recursos. Cuando queremos editar/añadir archivo de recursos y que los cambios ya se muestren. En este caso, se puede deployar como contenido.
    Esto se puede hacer cambiando la propiedad Build Action en el cuadro de propiedades del archivo de recursos de Visual Studio. Cambiarlo de recursos Embbeded Resource a Content.
    Deployar un recurso

Funcionamiento Standard

En MVC, se tienen dos opciones:

  • Poner los Recursos en el directorio App_LocalResources dentro de los directorios vistas. Y acceder a ellos mediante la función GetLocalResourceObject.
  • Poner los Recursos en el directorio App_GlobalResouces dentro del directorio raíz del proyecto. Y acceder a ellos mediante la función GetGlobalResourceObject.

Es posible reemplazar el comportamiento predeterminado de los recursos. En este artículo que encontré los conceptos claves para extender estas opciones limitadas.
Extending the ASP.NET 2.0 Resource-Provider Model
En este artículo explica como anular el comportamiento predeterminado; se describe como obtener los recursos de una base de datos en lugar de ficheros.

Sobreescribiendo el Funcionamiento Standard

Básicamente se debe interceptar la Factory que generará los proveedores de recursos. Y insertar el nuestro. A continuación se describen los pasos:

  1. Creación de la Factory de Proveedor de Recursos. Extendemos la clase ResourceProviderFactory que nos retornará nuestros proveedores de recursos. En el CreateGlobalResourceProvider/ CreateLocalResourceProvider se retornará nuestro proveedor.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Web.Compilation;
    
    namespace Infrastructure.Resource
    {
        using System;
        using System.Web.Compilation;
        using System.Web;
        using System.Globalization;
    
        public class FileResourceProviderFactory: System.Web.Compilation.ResourceProviderFactory
        {
    
            public FileResourceProviderFactory() : base()
            {
            }
    
            public override IResourceProvider CreateGlobalResourceProvider(string classKey)
            {
                return new FileResourceProvider(classKey);
            }
    
            public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
            {
                return new FileResourceProvider(virtualPath);
            }
        }
    }
    
  2. Creación del Proveedor. Nuestro proveedor debe extenderse interfaz IResourceProvider.
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Resources;
    using System.Text;
    using System.Web.Compilation;
    
    namespace Infrastructure.Resource
    {
        public class FileResourceProvider: IResourceProvider
        {
    
            private String _file;
    
            private static string Resolve(string path, CultureInfo culture)
            {
    
                var pathResource = path;
                if (null != culture)
                {
                    pathResource = PathResource(path, culture.Name);
                    if (!File.Exists(pathResource)) pathResource = PathResource(path, culture.TwoLetterISOLanguageName);
                    if (!File.Exists(pathResource)) pathResource = path;
                }
    
                return System.Web.HttpContext.Current.Server.MapPath(pathResource);
            }
    
            private static string PathResource(string path, String culture)
            {
                return path.Replace(System.IO.Path.GetFileName(path), string.Format("{0}.{1}{2}",
                                                                                    System.IO.Path.
                                                                                        GetFileNameWithoutExtension(
                                                                                            path),
                                                                                    culture,
                                                                                    System.IO.Path.GetExtension(path)));
            }
    
            public FileResourceProvider(String file)
            {
                _file = file;
    
            }
    
            public object GetObject(string resourceKey, System.Globalization.CultureInfo culture)
            {
                if (string.IsNullOrEmpty(resourceKey))
                {
                    throw new ArgumentNullException("resourceKey");
                }
    
                if (culture == null)
                {
                    culture = CultureInfo.CurrentUICulture;
                }
    
                var rsxrFilePathFr = GetCachedResourceReader(culture);
                return (from DictionaryEntry item in rsxrFilePathFr
                        where item.Key.Equals(resourceKey) select item.Value)
                        .FirstOrDefault();
            }
    
            public ResXResourceReader GetCachedResourceReader(CultureInfo culture)
            {
                var cache = System.Web.HttpContext.Current.Cache;
                ResXResourceReader resourceReader;
                var key = PathResource(_file, culture.Name);
                if (null == (resourceReader = cache[key] as ResXResourceReader))
                {
                    var fileByCulture = Resolve(_file, culture);
                    resourceReader = new ResXResourceReader(fileByCulture);
                    cache[key] = resourceReader;
                }
                return resourceReader;
            }
    
            public System.Resources.IResourceReader ResourceReader
            {
                get
                {
                    return GetCachedResourceReader(CultureInfo.CurrentUICulture);
                }
            }
        }
    }
    
  3. Sobreescribir comportamiento standard. Añadir en el Web.Config para que utilice nuestro sistema de acceso a recursos.
    <system.web>
    	...
    	<globalization resourceProviderFactoryType="Infrastructure.Resource.FileResourceProviderFactory, Infrastructure, Version=1.0.0.0, Culture=neutral" />
    	...
    </system.web>
    
Publicado en Desarrollo | Etiquetado , | Dejar un comentario

KISS (Keep it simple, stupid) y Patrones de Diseño

KISS (Keep it simple, stupid, Keet It short and Simple, Keep It Simple Software,…)

KISS es uno de los principales principios de desarrollo de software. Se puede resumir en que en igualdad de condiciones la solución más sencilla es probablemente la correcta. Los puntos básicos que lo definen son:

  • Partes sencillas y comprensibles, que permite trabajar fácilmente en grupos de trabajo.
  • Fácil mantenimiento y flexibilidad.
  • Con errores de fácil detección y corrección.
  • Descartar lo complejo e innecesario.

Patrones de Diseño

Los patrones de diseño son herramientas para mitigar la complejidad de los sistemas de software. Encapsulan modelos de resolver determinados tipos de problemas y es un elemento fundamental de las buenas practicas de programación. Básicamente ofrecen múltiples plantillas en las que basar nuestros desarrollos.

KISS vs Patrones de Diseño

El uso excesivo de patrones de diseño o patronitis  consigue el efecto contrario; aumenta en mesura la complejidad. Esto viola el principio KISS. Hay tendencia a usarlos cuando realmente no son necesarios.
Este articulo muestra un magnifico ejemplo sobre el abuso de patrones: The Abuse of Design Patterns in writing a Hello World Program.

La conclusión hemos de ser cuidadosos a la hora de introducir patrones de diseño;  el software no ha der ser más complejo de lo que necesite ser. En linea con XP hazlo simple y
evita grandes diseños; el diseño debe ocurrir en el proceso de desarrollo (cuando surgen los requerimientos del negocio).

Publicado en Desarrollo, Diseño | Etiquetado , , | Dejar un comentario

Gestión de errores en peticiones Ajax a MVC

Problema

Frecuentemente al realizar llamadas Ajax a servicios se pueden producir errores. Estos errores pueden ser tanto controlados como incontrolados. La cuestión es como realizamos una gestión simple de los mismos.

Propuesta

  • En el servidor
    Añadiremos el atributo [CodeExceptionToHttpFilter] en nuestros métodos ajax.
    Este filtro retornará el error en una respuesta de Http de la siguiente manera:

    • En el Status: 550 si el error es controlado y 551 si es incontrolado. El error es controlado cuando lo hemos generado con la clase CodeException.
    • En el Contenido: el mensaje de error.

    Adjunto ejemplo con un controlador con dos llamadas que generan un error controlado/incontrolado respectivamente.

        public class ThrowExceptionController: Controller
        {
    	    [CodeExceptionToHttpFilter]
            public int ThrowKnownError(CommandWrite command)
            {
    			throw new CodeException("Error Known");
    			return 0;
    		}
    
    	    [CodeExceptionToHttpFilter]
            public int ThrowError(CommandWrite command)
            {
    			throw new Exception("Error UnKnown");
    			return 0;
    		}
    	}
            
  • En el cliente
    En la parte cliente en la función de callback; por ejemplo en nuestro caso ignoramos los errores incontrolados y mostramos un mensaje para los controlado con el mensaje de error.

                $.ajax({
                    url: ...,
                    success: function(d){...},
                    error: function(xhr, status, error){ 
                        switch(xhr.status){
                            case 551:
                                return;
                                break;
                            case 550:
                                alert(xhr.responseText);
                                return;
                                break;
    		    }
                    }
                });	
    

Como funciona:

  • Definir la clase CodeException que encapsule los errores controlados. Esta será la manera que tendrá el atributo para discernir si es un error controlado o no.

        public class CodeException : System.Exception
        {
            public CodeException(String message): base(message)
            {
            }
        }
    
  • Definir la clase atributo. En MVC disponemos la clase FilterAttribute para gestionar los errores que se producen al llamar a los controladores. Nuestra clase CodeExceptionToHttpFilter hereda de la anterior, para gestionar de manera simple los errores en llamadas ajax.
    En el constructor de la clase se asigna la propiedad Order, que es la prioridad con la que actúa el filtro. Tendríamos que asignar una mayor a la que tengamos en el pipeline de MVC.

        public class CodeExceptionToHttpFilter : FilterAttribute, IExceptionFilter
        {
    
            public CodeExceptionToHttpFilter()
            {
                Order = 2;
            }
    
    		public void OnException(ExceptionContext filterContext)
            {
                var codeException = filterContext.Exception as CodeException;
                var response = filterContext.RequestContext.HttpContext.Response;
    			response.StatusCode = (codeException == null)? 550: 551;
                response.ContentType = MediaTypeNames.Text.Plain;
                response.Charset = "utf-8";
                response.Write(filter.Exception.Message);
                filterContext.ExceptionHandled = true;
                response.TrySkipIisCustomErrors = true;
            }
        }
    
Publicado en Desarrollo, web | Etiquetado , | Dejar un comentario

SignalR (III): El servidor se comunica con el cliente (la aplicación web al revés).

Normalmente las aplicaciones web utilizan el patrón de petición-respuesta. La comunicación fluye del cliente al servidor. Pensemos al revés, imaginemos la aplicación web desde el servidor. Si es posible!!! Hagamos que sea el servidor quien realice la petición al cliente. Básicamente se me ocurren dos tipos de escenarios:

  • Un proceso en ejecución en el servidor (ya sea lanzado por una petición o una tarea programada) del cual necesitamos ser notificados cuando finalice.
  • Arquitectura CQRS que queremos que se comporten de manera tradicional.

Tal como comentamos en anteriores entradas, SignalR permite las comunicaciones asíncronas. Cuando nuestro cliente se conecta al hub; automáticamente esta disponible para recibir peticiones.

Primera Propuesta

  1. En la parte cliente, insertar el código de conexión al Hub.
    $(function () {
        var backgroundProcessHub = $.connection.backgroundProcessHub;
    
        $.connection.hub.start();
    
        backgroundProcessHub.notifyTaskProcessed = function (message) {
    	alert(message);
        };
    });
    
  2. En la parte servidor, insertar en la tarea que finaliza en el servidor
    var hub = GlobalHost.ConnectionManager.GetHubContext<BackgroundProcessHub>();
    hub.Clients.NotifyTaskProcessed("Task Ended");
    

Esto funciona, únicamente tiene el inconveniente que todos los usuarios son notificados.

Segunda propuesta

SignalR tiene un registro de eventos en servidor; cuando se produce un conexión/desconexión con los clientes.
Aprovecharemos; esto para subscribir los clientes al servicio de notificación (de esta manera no nos hace falta realizar una petición de cliente al servidor con un método de suscripción ;-) ).

  1. En la parte cliente no se ha modificar nada, hacer lo mismo que en el paso anterior.
  2. En la parte servidor, añadir los eventos de conexión/desconexión en el hub.
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading;
    using System.Web;
    using Microsoft.WindowsAzure.ServiceRuntime;
    using SignalR.Hubs;
    using System.Security.Principal;
    
    namespace Example.Backoffice.Hubs
    {
        public class BackgroundProcessHub : Hub, IDisconnect, IConnected
        {
    
            public static readonly ConcurrentDictionary<string, string> Users = new ConcurrentDictionary<String, String>(StringComparer.OrdinalIgnoreCase);
    
    			public System.Threading.Tasks.Task Disconnect()
    			{
    				var userId =  WindowsIdentity.GetCurrent().Name;
    				Users.TryRemove(Context.ConnectionId, out userId);
    				return Clients[userId].leave(Context.ConnectionId);
    			}
        
    			public System.Threading.Tasks.Task Connect()
    			{
    				var userId =  WindowsIdentity.GetCurrent().Name;
    				Users.TryAdd(Context.ConnectionId, userId);
    				return Groups.Add(Context.ConnectionId, userId);
    			}
    
    			public System.Threading.Tasks.Task  Reconnect(IEnumerable<string> groups)
    			{
    				var userId =  WindowsIdentity.GetCurrent().Name;
    				Users.TryAdd(Context.ConnectionId, userId);
    				return Groups.Add(Context.ConnectionId, userId);
    			}
    	}
    }
    
  3. En la parte servidor, insertar en la tarea que finaliza en el servidor.
             var hub = GlobalHost.ConnectionManager.GetHubContext<BackgroundProcessHub>();
             var userName = "admin";
             if (BackgroundProcessHub.Users.Values.Any(x=>x == userName ))
                   hub.Clients[userName].NotifyTaskProcessed("Task Ended");
    

Notificaría al usuario, si este esta conectado. Podríamos sofisticar más el sistema, guardando la notificación para enviársela cuando se conectara de nuevo.

Tercera propuesta

  1. En la parte cliente no se ha modificar nada, hacer lo mismo que en el primer paso.
  2. En la parte hub del servidor no se ha de modificar nada, hacer lo mismo que en el paso anterior.
  3. En la parte servidor, insertar en la tarea que finaliza en el servidor.
             RegisterTaskEnded("admin","Task Ended");
    
  4. En la parte servidor, añadir una tarea
    using System;
    using System.Linq;
    using SignalR;
    
    namespace Example.Backoffice.Hubs
    {
    
        public static class BackGroundProcessJob
        {
    
            public static void DoWork()
            {
    
                var hub = GlobalHost.ConnectionManager.GetHubContext<BackgroundProcessHub>();
                foreach(var group in BackgroundProcessHub.Users.Values.Distinct())
                {
                    var result = GetTaskEnded((String) group);
                    if (result != null) hub.Clients[group].NotifyTaskProcessed(result.Message);
                }                
            }
        }
    }
    

    La tarea la lanzamos con la clase JobHost con un intervalo de 5 segundos.

                ThreadPool.QueueUserWorkItem(
                    state => new JobHost(null, null).Run(BackGroundProcessJob.DoWork, 5000));
    

Esta clase JobHost se encuentra descrita en articulo anterior Ejecutando un servicio en BackGround en IIS (Azure).

Ahora tenemos una aplicación totalmente pensada al revés. Es el servidor que genera peticiones en el cliente, cuando el usuario usa o se conecta a la aplicación.

Publicado en Desarrollo, SignalR, web | Etiquetado | Dejar un comentario

SignalR (II): SignalR en Windows Azure

Problema

Al trabajar en Azure, cuando necesitamos trabajar con múltiples instancias, SignalR no puede registrar el hub de comunicación dado que las peticiones de conexión pueden ir independiente a las diferentes instancias.
Afortunadamente existe un add-on de Signal-R que permite trabajar en Azure; se basa en la utilización del servicio de bus que provee el propio Azure. De esta manera al registrarse una conexión esta se sincroniza para las diferentes instancias.

Instalación

  1. Crear el servicio Service Bus en la consola de Azure

    Opción para Data de Alta un Tema de Service Bus en Azure

  2. El add-on lo tenemos disponible en GitHub desde donde podemos descargarlo. Aunque la forma más fácil de instalarla es desde la consola de Nutget, mediante el siguiente comando:
    Install-Package SignalR.WindowsAzureServiceBus

    Instalar signalR.WindowsAzureServiceBus vía NutGet

  3. En el app_start de la aplicación registramos nuestra configuración de Azure Service Bus con el AddOn.
            if (RoleEnvironment.IsAvailable)
            {
    			var roleName = RoleEnvironment.CurrentRoleInstance.Role.Name;
    			if (RoleEnvironment.Roles[roleName].Instances.Count > 1)
    			{
    
    				const string serviceBusNamespace = "BusNamespace";
    				const string serviceBusAccount = "BusAccount";
    				const string serviceBusAccountKey = "BusAccountKey";
    				const string topicPathPrefix = "signalr";
    				const int numberOfTopics = 1;
    
    				SignalR.GlobalHost.DependencyResolver.UseWindowsAzureServiceBus(
    					serviceBusNamespace, /* the service bus namespace prefix */
    					serviceBusAccount, /* "owner" or some other account */
    					serviceBusAccountKey, /* the key, which can be copied from the Windows Azure portal */
    					topicPathPrefix, /* the prefix applied to the name of each topic used */
    					numberOfTopics /* how many topics across which messages will be sharded */
    					);
                }
            }
    

    Únicamente lo registramos si estamos en Azure y tenemos más de una instancia deployada, de esta manera evitamos realizar peticiones al Service Bus sino es necesario.

Publicado en Azure, Desarrollo, SignalR | Etiquetado | Dejar un comentario