Que nunca falte un registro de actividad en tu aplicación. Nunca.

La semana pasada un cliente me llamó debido a que una de sus aplicaciones recién lanzada estaba un tanto inestable. Quiero compartir hoy con ustedes la recomendación que le di, la mas simple y mas efectiva que recuerdo haber dado.

Mas allá de revisar algunas cuestiones directamente relacionadas con las inestabilidades reportadas por usuarios finales, lo primero que les pedí es que me mostraran los logs para enterarme de que no los tenían.

Hoy no hay excusas para no tener información de este tipo en la plataforma .NET, todos los tipos de aplicaciones (Web, Desktop, Servicios, etc.) proveen un mecanismo centralizado de

Aquí algunos consejos básicos:

  • Registren todas las excepciones (básico)
  • No es necesario utilizar try – catch innecesariamente en todo su código, es suficiente con engancharse en el handler centralizado.
  • Registre información con distinto nivel de detalle (los niveles corresponden a log4net pero los demás utilitarios tienen niveles similares), al menos:
    • ERROR: las excepciones, deje que el framework se encargue de mostrar mensajes, callstacks, etc. También registre errores de formato, de protocolo, etc, aunque también podría utilizarse el nivel WARNING (o WARN).
    • INFO: algunos puntos importantes del funcionamiento de la aplicación tales como el inicio, datos de configuración utilizados, finalización, etc.
    • DEBUG: en esta modalidad puede registrarse información mas detallada del funcionamiento de la aplicación.
  • Configure correctamente el nivel de registro en cada ambiente. Generalmente uso nivel INFO en ambientes de producción (que también registra WARN, ERROR y CRITICAL) y nivel DEBUG en ambientes de desarrollo.
  • Use herramientas sencillas (log4net, NLog) y registre la información en simple archivos de texto. Nunca me resulto cómodo utilizar el registro de eventos de Windows.

Para aplicaciones ASP.NET

protected void Application_Start()
{
    Error += Application_Error;
}

private void Application_Error(    object sender, EventArgs e)

{
Exception ex = Server.GetLastError();
log.Error("Application_Error", ex);
Server.ClearError();
}

Para aplicaciones Desktop

static void Main()
{
    AppDomain.CurrentDomain.UnhandledException +=
        UnhandledExceptionHandler;

    Application.Run(new Principal());
}
private static void UnhandledExceptionHandler(
    object sender, UnhandledExceptionEventArgs args)
{
    var e = (Exception)args.ExceptionObject;

    log.Error("Application_Error", ex);

    if (MessageBox.Show("Reporte este error:\n" +         e + "\n\n¿Desea copiarlo al portapapeles?",
        "Error en Clasificador",
        MessageBoxButtons.YesNo,         MessageBoxIcon.Error) == DialogResult.Yes)
    {
        Clipboard.SetText(e.ToString());
    }
    Environment.Exit(1);
}

Mas informacion:

Publicado en arquitectura, tips | 3 comentarios

¿Cuándo estaremos listos para Azure?

En la presentación de ayer en CodeCamp/09 uno de los colegas asistentes preguntó cuando estaría liberado Windows Azure para su uso en aplicaciones en producción.

No tenia idea de la respuesta en ese momento (ahora se que van a anunciarlo en Noviembre en el PDC), sin embargo salto a mi mente otra pregunta: ¿Cuándo estaremos nosotros listos para Azure?

Realmente es un entorno de ejecución diferente del que conocemos y que nos demandará bastante tiempo entender, dominar y encontrar la mejor manera de hacer las cosas.

También importante entender que Azure apunta a resolver problemas que muchos aún no tienen y, seguramente, las concesiones de diseño que se han hecho parecerán limitaciones excesivas. Suena raro que no tengamos file system, que no haya soporte para sesiones in process, que no existan otras comodidades similares pero son “pérdidas” razonables para aplicaciones masivamente escalables.

Estoy seguro de que Azure estará listo mucho tiempo antes que nosotros.

Sean buenos.

Publicado en aspnet, azure, codecamp | Deja un comentario

Azure en CodeCamp09 – Salió bien

Hace minutos terminamos con Mariano la conferencia sobre Azure en CodeCamp. Todo salió bien, un poco ajustados con el tiempo.

Dejo aquí los links de la charla.

Windows Azure Platform:
http://www.azure.com/

Space de la charla en Assembla:
https://www.assembla.com/wiki/show/prx-guamini

El repositorio con todos los artefactos de la charla:
http://code.assembla.com/prx-guamini/subversion/nodes/trunk

Gracias por venir!

Sean buenos

Publicado en azure, codecamp | Deja un comentario

Configuración perdida de un Azure WorkerRole

Resulta que en la versión July 2009 CTP de las Windows Azure Tools for Microsoft Visual Studio se escapo un error. El proceso de build no copia el App.config del proyecto del WorkerRole.

Tiene solución, no se preocupen. Solo busquen en Google.

Sean buenos.

Publicado en azure | Deja un comentario

Azure mejora

Una buena noticia para el sufrido administrador de servicios Azure. El proceso de actualización de un servicio ahora permite realizar la operación en un solo paso: Upgrade.

Hasta hace unos días era necesario seguir el siguiente proceso: a) detener la aplicación; b) borrarla; c) subir la nueva y d) ponerla en marcha.

Aquí se ve la nueva opción:

image

Aquí pueden ver la pantalla que informa sobre el avance del proceso de actualización.

image

Aún hay que esperar varios minutos para que el proceso termine pero al menos puedo dedicarme a otra cosa.

Sean buenos.

Publicado en azure, codecamp | Deja un comentario

CodeCamp 2009

El viernes pasado me enteré de que la charla con que nos postulamos para la edición 2009 de CodeCamp junto con Mariano había sido aceptada.

El título preliminar es "Cloud Computing – Explorando Windows Azure Services" asi que estaremos investigando más profundamente la plataforma Azure.

Publicaré en este blog los avances en el proceso, los interesados estan invitados a volver.

Nos vemos allá.

Publicado en azure, codecamp | Deja un comentario

Aggregates and repositories

This an informal post about this message in the DDD  list.
I really know that this should be answered by a DDD guru but, seeing that no one showed up, I’ll try to fill the space.
UPDATE Nov/19: Added section titles, service implementation to answer Tom’s comment, changed wrong method name from GetActorsNotAssociatedWithPictures to GetActorsNotAssociatedWithMovies (thanks Tom).
Specifications
Let’s say we have this domain: movies and actors acting in that movies.
The goal of this article is to outline how to design this simple domain, to define aggregates (and identify aggregate roots) and the corresponding repositories.
The domain model specifications:
  • Many actors can act in a movie
  • An actor could act in many movies
  • I should be able to remove movies (actors shouldn’t be removed)
  • I should be able to remove and actor (provided it is not associated with any movie)
Let me detail aside the query specifications:
  • I want to retrieve all the movies in what an actor participated
  • I want to retrieve all the movies older that 10 years
Model design
Now let me design the model. We should have at least two objects, Movie and Actor, so let’s start with that classes:
NOTE: code is in C# without IDE assistance and incomplete, for example, trivial accessors omitted.
public class Movie {
private string name;
private int yearPresented; 
}
public class Actor {
private string name;
private int yearBorn;
}
    OK, pretty simple. Now I’ll try to connect actors and movies. Let’s look for a classic in IMBD, Gone with the wind. If you scroll a page you can see the actors on that movie, they are the Cast, we could build an object Cast but I’ll choose a simple path, more on that later.
    Let’s see, for example, for the Scarlett O’Hara character, the actress was Vivien Leigh.
    OK, it’s enough of IMDB, let’s go to the model but let me clarify, it’s very important to make the best effort to find the formal (and ubiquitous) language, that’s why I gone to IMDB.
    So, seems like a good first step is to build the Character object.
    public class Character {
    private Actor actor;
    private string name;
    }
    Now is time to define aggregates, I see two:
    • The Actor aggregate including just an Actor instance as aggregate root.
    • The Movie aggregate including a Movie instance as an aggregate root and one or more Character instances. Each Character instance references one Actor aggregate (kind of foreign key in RDMS parlance).
    Let me do another round adding some code to my classes (I’m showing code in the  Fowler‘s way):
    Mental note: if Martin changed that old picture in his home page I think it’s time to change mine, it’s 10 years old.
    public class Movie {

    private IList cast = new List();
    public Character AddCharacter(Actor actor, string characterName) {
    Character newCharacter = 
        new Character(this, actor, string characterName);
    cast.Add(newCharacter):
    return newCharacter;
    }
    … 
    }
    And the Character class
    public class Character {

    internal Character(Movie movie, Actor actor, string name) {
    this.movie = movie;
    this.actor = actor;
      this.name = name;
    }
    private Movie movie;

    }
    Repositories design
    Now I have this domain model pretty much I like it, let me outline the repositories interfaces (one for each aggregate). As you will see, I use the “one method per query” approach, I could use the more concise GetByCriteria approach but it tends to force me to leak the query API to the model.
    public interface MovieRepository {
    Movie GetByName(string name);
    (1) IList GetByActor(Actor actor);
    (2) IList GetByCharacterName(string characterName);
    (3) IList GetAgedMoreThan(int years);
    (4) void Delete(Movie movie)
    }
    public interface ActorRepository {
    Actor GetByName(string name);
    (5) IList GetActorsNotAssociatedWithMovies();
    (6) void Delete(Actor actor)
    }
    (1) Should retrieve movies doing a simple join to compare the actor. Pretty easy to do with a good ORM.
    (2) and (3) This is a easy queries as well.
    (4) Should delete the Movie instances and all the Character instances associated. Actor instances remain untouched.
    (5) Not a difficult query, after obtaining the Actors list we could use (6) to delete un referenced actors.
    Services design (some of them)
    Tom is curious on how to implement the “I should be able to remove and actor (provided it is not associated with any movie)” specification.
    I would like to put this into a more real specification like that: “I should be able to purge old pictures and unreferenced actors”. For this spec I would build a maintenance domain service like that:
    public interface CatalogMaintenanceService {
      public void DeleteDataOlderThan(int yearsOld) {
        IList oldMovies = moviesRepository.GetAgedMoreThan(yearsOld);
        foreach (Movie movie in oldMovies)
          moviesRepository.Delete(movie);

        IList orphanedActors =
          actorsRepository.GetActorsNotAssociatedWithPictures();
        foreach (Actor actor in orphanedActors)
          actorsRepository.Delete(actor);
      }
    }
    This service call should be wrapped by a unit of work. The implementation depends on the persistence layer and application platform, for example, in a Web application with NHibernate, it’s usually implemented with a HttpModule.
    Please, feel free to ask any question, I would try to answer according my possibilities.
    See you
    Publicado en ddd aggregate repository | 7 comentarios

    Refactoring State/Strategy

    Este post nació a partir de una consulta en la lista de Patrones del Grupo de Usuarios Microsoft.

    En mi opinión y partiendo de la poca información que puede intercambiarse en las listas sobre el problema concreto, una de las posibilidades es el uso combinado de los patrones State/Strategy (un refactoring definido por Fowler en su libro, un esquema puede verse aquí).

    Brevemente, Leandro necesitaba calcular un impuesto sobre una transacción y el algoritmo de cálculo depende del tipo de cliente y de algun dato adicional (supongamos que entendí correctamente el problema).

    La condición para aplicar State es que tenga un caso en el cual el comportamiento del cliente (en este caso, el tipo de liquidador de impuesto) dependa del estado (de un estado o situacion) que el cliente tenga en ese momento. Otra de las condiciones es tener un switch que reemplazar.

    Bueno, ahi va algo de codigo (escrito directamente en el blog, sería raro que compile!!!)…

    // La clase Cliente
    public class Cliente {
       // Este es el estado en cuestion, en principio puede ser privado
       private TipoCliente tipoCliente;
    
       // Estamos suponiendo, tambien, que cuando se crea el cliente
       // queda ya configurado el tipo, aunque nada impide que
       // este estado cambie durante la vida del cliente.
       public Cliente( TipoCliente tipoCliente ) {
          this.tipoCliente = tipoCliente;
       }
    }
    
    // Ahora definimos el tipo, una clase abstracta
    public class TipoCliente {
    }
    
    // Un tipo de cliente concreto
    public class TipoClienteExtranjero : TipoCliente {
    }
    
    // Otro tipo de cliente concreto
    public class TipoClienteEmpresa : TipoCliente {
    }
    

    Bien, hasta aca tenemos la estructura basica armada, ahora veamos como resolvemos el tema de obtener el tipo de calculador de impuesto.

    // Primero definimos la interfaz para el calculador
    public interface ICalculadorImpuesto {
       // Este metodo deberia calcular el impuesto sobre
       // la transaccion y devolver un objeto que defina
       // el impuesto aplicado (no defini estas dos clases)
       Impuesto CalcularImpuesto( Transaccion trx );
    }
    
    // Ahora definimos un calculador facil, para empresas extranjeras
    public class CalculadorImpuestoExento : ICalculadorImpuesto {
       public Impuesto CalcularImpuesto( Transaccion trx ) {
          // Aca, de paso aplicamos el patron SpecialCase,
          // algo parecido al DateTime.MinValue
          return Impuesto.Zero;
       }
    }
    

    Esta bien, hagamos uno un poco mas complejo

    // El que calcula el impuesto a los movimiendos de debito
    // y credito para ctas ctes...
    public class CalculadorImpuestoLey25413CtaCte : ICalculadorImpuesto {
       public Impuesto CalcularImpuesto( Transaccion trx ) {
          return new Impuesto( trx.Monto * 0.012 );
       }
    }
    
    // y el impuesto a los movimiendos de debito y credito para
    // cajas de ahorro
    public class CalculadorImpuestoLey25413CA : ICalculadorImpuesto {
       public Impuesto CalcularImpuesto( Transaccion trx ) {
          return new Impuesto( trx.Monto * 0.006 );
       }
    }
    

    Ahora apliquemos esto en nuestra clase Cliente

    // La clase Cliente
    public class Cliente {
       ...
       // Este metodo delega en el metodo del tipo de cliente
       public ICalculadorImpuesto ObtenerCalculadorImpuesto() {
          return tipoCliente.ObtenerCalculadorImpuesto( this );
       }
       ...
    }
    

    Falta ahora modificar las clases de tipos de cliente

    // En la clase abstracta
    public class TipoCliente {
       public abstract ICalculadorImpuesto ObtenerCalculadorImpuesto( Cliente cliente );
    }
    
    // Un tipo de cliente concreto
    public class TipoClienteExtranjero : TipoCliente {
       public override ICalculadorImpuesto ObtenerCalculadorImpuesto( Cliente cliente ) {
          // Aqui devuelvo el claculador que necesito
          // Para extranjeros, exento, por ejemplo
          return new CalculadorImpuestoExento();
       }
    }
    
    // Otro tipo de cliente concreto
    public class TipoClienteEmpresa : TipoCliente {
       public override ICalculadorImpuesto ObtenerCalculadorImpuesto( Cliente cliente ) {
       // Aqui devuelvo el claculador que necesito
       // Aqui vemos el caso en que el State consulta al
       // objeto que lo contiene para decidir.
       // ( implementacion sencilla! )
       if ( cliente.TipoCuenta == "CtaCte" )
          return new CalculadorImpuestoLey25413CtaCte();
       else
          return new CalculadorImpuestoLey25413CA();
       }
    }
    

    Por ultimo, veamos como se usaria esto en el ejemplo que se me ocurre, por ejemplo, en metodo que tiene que calcular el impuesto de la transaccion:

    public void RegistraTransaccion( Cliente cliente, Transaccion trx ) {
       ICalculadorImpuesto ci = cliente.ObtenerCalculadorImpuesto();
       Impuesto imp = ci.CalcularImpuesto( trx );
       RegistrarTansaccionEImpuesto( trx, imp );
    }
    

    Bueno, espero que esto ayude. Aqui el patron State esta en la familia de clases TipoCliente y el Strategy en el calculador de impuestos.

    Publicado en Uncategorized | Deja un comentario

    TDD y Arquitectura en Rosario

    Luego de tres años, el último viernes y sábado estuve en Rosario compartiendo buenos momentos con colegas de esa ciudad.

    Lo mejor (y el verdadero motivo del viaje) fue la cena y el almuerzo (pizza y asado respectivamente).

    El viernes los aburrí un poco con una sesión sobre TDD. Si alguien esta interesado puede acceder a la grabación aquí.

    También pueden descargar el archivo WMV desde este link (click secundario y guardar destino – el tamaño aproximado es 80MB).

    El sábado (mientras haciamos tiempo antes de ir a la parrilla), hablamos de MonoRail.

    Muchas gracias a mis colegas rosarinos. Nos veremos pronto.

    Publicado en tdd ood | 2 comentarios

    Miercoles 20 en Resistencia

    El miércoles 20 visitaré Resistencia, la capital de la provincia del Chaco por primera vez.

    Participaré en un evento en el marco de la Gira Nacional organizada por el MUG. Me toca exponer sobre arquitectura de aplicaciones Web y en esta ocación presentaré mis experimentos con Castle Project, en especial con los subproyectos MonoRail y ActiveRecord.

    En otra entrada publicare el material.

    Nos vemos.

    Publicado en Uncategorized | 1 comentario