August 2011 - Artículos

Al menos en mi país, a los principiantes de esta industria se los llama juniors. Esa es la categorización con la que se ingresa a una empresa si no se cuenta con experiencia y conocimientos demostrables que justifiquen una categoría superior. La idea es que con el tiempo y luego de ir adquiriendo los conocimientos necesarios, un junior va a ir progresando paulatinamente transitando por las distintas categorías hasta convertirse en un senior o similar. La verdad es que en muchos casos se espera que esa metamorfosis ocurra de manera mágica simplemente con el paso del tiempo, cosa que rara vez sucede.

Nadie contrata a un principiante para formarlo, muy por el contrario, se los contrata con la idea ponerlos a producir “código real” en “proyectos reales” para “clientes reales". Y así sucede, a poco de entrar ya están codificando como pueden, en muchos casos sin guía alguna y en el mejor de los casos con algún que otro referente al que le pueden consultar algún que otro tema puntual, pero no mucho más. Por esto es que muchos desarrolladores van aprendiendo distintas tecnologías (las que les demanda el proyecto de turno), frameworks, estilos, modas y demás pero nunca pueden dar el salto ya que nadie, ni la universidad ni las empresas los preparan para ello.

Si al igual que yo, te pasas varias horas a la semana explicando una y otra vez el por qué tragarse las excepciones es una mala idea, sabrás bien de que hablo. No obstante, esas explicaciones, por mejor intencionadas y didácticas que sean resultan siempre insuficientes y tardías. Simplemente esa no es la manera de enseñar ni de aprender y en la cabeza del principiante, todas esas enseñanzas parecen conformar más una serie de tips disconexos que una estructura sólida de conocimiento sobre el estado del arte.

Por eso, en mi experiencia, y salvo contadas excepciones, la única manera de adquirir los conocimientos necesarios para nuestra profesión es con la ayuda de un buen maestro y luego sí, leyendo (algo así como hacer la tarea). Sin un maestro, y en lo posible uno bueno, muchos de los principiantes terminan siendo juniors por siempre.

Leyendo el artículo de Javier Torrecilla  Patrón Unit of Work (UoW) o Unidad de Trabajo me acordé que en 2007 había escrito sobre este patrón y quiero rescatarlo aquí:
 
Este es uno de los patrones más útiles (desde mi punto de vista) con que me he encontrado en el libro "Patterns of Enterprise Application Architecture" (P of EAA) de Martin Fowler. Como dice Martin en su libro este patrón:
"Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems."
Este patrón se utiliza entonces para trabajar con un conjunto de objetos persistentes que deben tratarse como una "unidad" de trabajo, almacenándose en una base de datos de manera atómica. Este patrón es el encargado de hacer el seguimiento de todos aquellos objetos que son nuevos, y que por lo tanto deben persistirse, todos los objetos que han sido modificados y que deben actualizarse en la base de datos y todos los que han sido borrados y deben quitarse de la base de datos.
 
Mediante la implementación del patrón UnitOfWork, se logra una disminución de la cantidad de idas y vueltas hacia la base de datos ya que los cambios se realizan por lotes (o unidades de trabajo) redundando en incremento en el rendimiento del sistema porque muchas veces el cuello de botella de las aplicaciones se encuentra en la red de datos. Otro aspecto interesante es que posibilita el esquema de trabajo desconectado con bloqueos optimistas sobre la base de datos, es decir, no se mantienen bloqueados los registros por largos períodos de tiempo sino que se abre una conexión, se inicia una transacción, se hacen los cambios, se hace commit y se libera la conexión todo esto en muy pero muy poco tiempo.
De ser necesaria la incorporación de un control de concurrencia sobre los registros que serán afectados durante la transacción, de modo de mantener la consistencia de los datos, este patrón nos brinda el ámbito adecuado en donde implementarlo.
Veamos un caso concreto, el famoso ejemplo de la factura. Existe una factura que el usuario debe modificar, entonces traemos desde la base de datos la factura con sus ítems (de factura) cargados, luego, una vez que lo presentamos en pantalla, el usuario hace lo siguiente:
  • Agrega dos ítems más a la lista de ítems,
  • Modifica el importe y/o la cantidad de productos de uno de los ítems y finalmente,
  • Elimina algún que otro ítem.

En este caso, cuando deben persistirse los datos? apenas agrega, modifica o elimina un ítem? o debe tratarse a la factura como una unidad y persistir todo junto? Si algo falla,... debe guardarse el resto? que sucede si otro usuario (además del que estamos hablando) estaba trabajando con el mismo documento al mismo tiempo pero guardó primero? como sabemos que fue lo que modificó el usuario y que, por lo tanto, debemos guardar? guardamos primero la factura y luego sus ítems o al revés?

Todas las respuestas a estas preguntas (que considero que las fuiste contestando) se resuelven mediante la implementación de este patrón.

Voy a explicarlo un poco. El Unit Of Work, como se observa en la figura de arriba, se implementa mediante una única clase la cual tendrá al menos tres listas, una lista para los objetos nuevos que deben guardarse en la db, otra lista para los objetos que han sido modificados y que por lo tanto deben modificarse en la db y, otra lista para los objetos que deben eliminarse. Opcionalmente, o mejor dicho, según la implementación lo requiera, puede contener una cuarta lista para almacenar clones de los objetos que se traen desde la base de datos, de manera que antes de persistir un objeto pueda corroborar, mediante estos clones de los objetos originales, que los registros correspondientes no han sido modificados por otro usuario.

Una razón para leer el libro y no solo conformarse con este artículo es que Fowler hace un análisis exhaustivo de este patrón (y de todos sus patrones), sus formas de implementar, cuando implementarlos, pros y contras y sobre todo, lo que a mi más me gustó fueron los distintos intentos por hacer esta clase lo más transparente posible para el programador. Así, por ejemplo, analiza la posibilidad de que todas las clases persistibles hereden de una clase base que automáticamente las registre como nuevas en el contenedor, que cuando se invoque el setter de una propiedad, esta notifique al UnitOfWork correspondiente para que la registre como dirty, etc.

Acá dejo una implementación sencillísima de UnitOfWork sin control de concurrencias y en colaboración con un DataMapper trivial (actualización: el artículo original tenía un proyecto adjunto el cual he perdido, por esta razón el DataMapper no está presente pero lo que originalmente hacía era retornar la sentencia sql necesaria para insertar, modificar y borrar los objetos en la base de datos).

using System;
using System.Collections.Generic;
using System.Text;
using System.Transactions;
using System.Data.SqlClient;
using System.Configuration;
using System.Threading;
using System.Resources;
using ConsoleApplication7;

namespace Patterns
{
    // Nuestra clase UnitOfWork (Martin Fowler)
    public class UnitOfWork
    {
        // Aquí estan las tres listas de las que hablamos
        // Ademas de estas puede existir una cuarta que almacene
        // los objetos limpios (o que se leyeron de la db)
        List<IBusinessObject> newObjects;
        List<IBusinessObject> dirtyObjects;
        List<IBusinessObject> removedObjects;

        // Creamos las listas en este constructor
        public UnitOfWork()
        {
            newObjects = new List<IBusinessObject>();
            dirtyObjects = new List<IBusinessObject>();
            removedObjects = new List<IBusinessObject>();
        }

        public void New(IBusinessObject bo)
        {
            Guard.NotNull(Resources.parameter_is_null, "bo", bo);
            Guard.IsTrue(Resources.object_is_dirty, dirtyObjects.Contains(bo));
            Guard.IsTrue(Resources.object_is_deleted, removedObjects.Contains(bo));
            Guard.IsTrue(Resources.object_is_already_inserted, newObjects.Contains(bo));

            newObjects.Add(bo);
        }

        public void Remove(IBusinessObject bo)
        {
            Guard.NotNull(Resources.parameter_is_null, "bo", bo);
            if (newObjects.Remove(bo)) return;
            dirtyObjects.Remove(bo);

            if (!removedObjects.Contains(bo))
                removedObjects.Add(bo);
        }

        public void Update(IBusinessObject bo)
        {
            Guard.NotNull(Resources.parameter_is_null, "bo", bo);
            Guard.IsTrue(Resources.object_is_deleted, removedObjects.Contains(bo));

            if (!newObjects.Contains(bo) && !dirtyObjects.Contains(bo))
                dirtyObjects.Add(bo);
        }

        // El método Commit es el encargado de iniciar las transacciones,
        // y realizar la invocación a la base de datos.
        public void Commit()
        {
            string connectString = string.Empty;
            using (TransactionScope transactionScope = new TransactionScope())
            {
                connectString = ConfigurationManager.ConnectionStrings["Patterns"].ConnectionString;

                using (SqlConnection connection = new SqlConnection(connectString))
                {
                    // Construimos las sentencias SQL para luego pasárselas a la DB
                    // mediante un command. Para esto, en .Net hay que hacerlo separando
                    // las sentencias con un punto y como (;)
                    StringBuilder stringBuilder = new StringBuilder();

                    foreach (IBusinessObject bo in newObjects)
                        stringBuilder.Append(Mapper.Instance.Insert(bo) + ";");

                    foreach (IBusinessObject bo in dirtyObjects)
                        stringBuilder.Append(Mapper.Instance.Update(bo) + ";");

                    foreach (IBusinessObject bo in removedObjects)
                        stringBuilder.Append(Mapper.Instance.Delete(bo) + ";");

                    string command = stringBuilder.ToString();

                    // Abre la conexió, crea el comando con las sentencias SQL
                    // e invoca al RDBMS.
                    connection.Open();
                    SqlCommand command1 = new SqlCommand(command, connection);
                    command1.ExecuteNonQuery();

                    // Limpia las listas si todo estuvo bien.
                    ClearAll();
                }
                transactionScope.Complete();
            }
        }

        private void ClearAll()
        {
            newObjects.Clear();
            dirtyObjects.Clear();
            removedObjects.Clear();
        }
    }
}

Copio esta entrada del blog de Javier Garzás porque me ha hecho reír muchísimo

Publicado 6/8/2011 4:28 por Lucas Ontivero | 1 comment(s)
Archivado en:

A mi anterior entrada la titulé “Las plantillas T4 son basura” cosa que respondió más a mi estado de bronca contra éstas que a su verdadero valor como herramienta. Muchos me preguntaron sobre el por qué de tal calificación y la verdad es que ese por qué es demasiado largo de explicar pero voy a mostrar la punta del ovillo para que a quien le interese pueda descubrirlo por sí solo.

Veamos un par de ejemplo muy sencillos, el primero es crear un CVS partir de una array definido como sigue:

image

Lo que queremos obtener es lo siguiente:

image

T4

StringTemplate

image image

Ambos dan por resultado exactamente la misma salida. Ahora, como no soy experto en ninguno de los dos, doy por seguro que existen mejores maneras de hacerlo tanto usando T4 como StringTemplate. Pero, si damos por válido el ejemplo, podemos ver claramente la diferencia en legibilidad y, aunque el ejemplo es muy pequeños, en mantenibilidad la diferencia también es evidente.

Pero veamos algo levemente más complejo, solo un poquito más complejo, generemos una tabla html para ver algunas diferencias. Aunque con StringTemplate lo haría así:

image

Voy a refactorizarla y compararla en la tabla de abajo.

T4

StringTemplate

image image

Nuevamente aquí estoy seguro de que existen mejores maneras de hacerlo tanto con T4 como con StringTemplate pero bueno, que valga el ejemplo. así que veamos… (suspiro prolongado) por donde empezar….?. quizás deba aclarar que mientras que la plantilla de la derecha está completa, a la de la izquierda  le quité varios renglones con directivas varias del tipo <@template> y <@output>. 

Antes de comenzar pregunto: ¿soy yo, o en la plantilla del lado izquierdo hay mucho amarillito?

Bueno, ahora si, lo primero es que dado que en la plantilla de la izquierda tenemos código, este puede explotar con alguna exception. Por ejemplo, que sucede si data es NULL? Sí, tendremos una bonita exception y entonces quizás debamos depurar la plantilla. Sí, leíste bien: depurar una plantilla! por más ridículo que suene, es algo que probablemente debamos hacer. En cambio, con la plantilla de la derecha obtendremos simplemente una tabla html vacía, no muy útil pero válida al fin.

Otro punto es que cualquiera que haya usado T4 ha sufrido alguna vez la pérdida de alguna etiqueta de apertura o cierre y ha perdido la vista tratando de encontrar donde es que le falta abrir o cerrar un tag. Y es que en semejante sopa de tags cualquiera se pierde. Por eso es común ver comentarios en la plantilla al estilo: // cierra foreach ya que uno termina preguntándose ¿que es lo que está cerrando esta llave? Bueno, esa llave está cerrando un foreach!

En cuanto a la calidad de la salida solo puedo decir que con la plantilla de la derecha, sin ningún esfuerzo adicional, obtengo un html perfectamente tabulado mientras que con la de la derecha….uhmmm, bueno… requiere algún trabajito adicional que se resuelve agregando más código.

Tal vez algo que me agrada de StringTemplate es que nos mantiene protegidos de sus mecanismos internos, es decir, uno no está obligado, y de hecho no debería estarlo, a entender cómo es que funciona internamente. Pero T4 si nos obliga, solo basta ver los distintos tipos de bloques que existen para darse cuenta: ¿Standard feature block?, ¿Class feature block? Además deben ir en un orden específico o no compilan. Sí, esas son las tuberías saliéndose hacia afuera a las que me refiero, el creador debe estar consciente de a dónde va a ir a parar lo que pone en cada block según el tipo de bloque, debe entender o imaginar como es la clase que T4 genera por detrás. Por tal motivo, T4 es solo para programadores.

Corto acá pero antes debo aclarar que no basta solo con “ver” las diferencias sino con “entender” las diferencias, entender todo lo conceptual que hay por detrás de la elegancia de StringTemplate (o de la inelegancia de T4).

Por muchos años he usado StringTemplate como motor de plantillas para la generación automática de código por su sencillez, elegancia y su increíblemente bien pensada separación entre lógica y plantilla. En StringTemplate uno no pude mezclar código con segmentos de plantilla y eso hace que las plantillas sean limpias, claras y totalmente independientes de cualquier lenguaje o plataforma (lamentablemente requieren un runtime).

Por motivos que escapan a mi libre elección, últimamente he tenido que utilizar plantillas T4 para generar algunos módulos del sistema en que trabajo a partir de metadatos almacenados en XMLs, la verdad es que es una experiencia horrorosa. Todo ese código mezclado con el output utilizando distintos tipos de tags (<# <#= <#+) que exponen la el sistema de cañerias es una pesadilla. La solución aparente es refactorizar las platillas para separar lo más posible el código de los segmentos de salida pero la verdad es que luego de un gran esfuerzo, si bien ya no provoca vómitos, sigue generando náuseas.

A los creadores les preguntaría: ¿qué hemos hecho para merecer esto?