22 diciembre 2011

"Developability"

Existen numerosos atributos de calidad, e incluso clasificaciones estándar de ellos.

¿Para que sirven estas clasificaciones?

Por un lado sirven para darle a los diseñadores de sistemas un punto de partida. Algo que permita dar respuesta a la pregunta: "entre las características que requiere nuestro sistema ¿nos estaremos olvidando de algo?". Por otro sirven para que los investigadores de arquitectura de software puedan definir métricas y estilos de solución.

Y por otro, sirven para que yo tenga una excusa por donde empezar este post :)

Estaba pensando que en estas clasificaciones hay un atributo de calidad perdido, que voy a bautizar como "Developability", y voy a definir así: la capacidad un sistema para reflejar los cambios introducidos durante el desarrollo.

Después de todo si en Java existen productos como JRebel y frameworks como Play anuncian sus bondades con frases como: "No need to compile, deploy or restart the server"; creo que existe una necesidad de tener la característica de "developability" en mente a la hora de pensar en el diseño de un sistema.

¿No es lo mismo que Maintainability?
Es parte de la mantenibilidad, en el sentido que forma parte de la facilidad de cambio de un sistema.

Pero muchas veces cuando se habla de mantenibilidad se hace referencia a cuan fácil es introducir nueva funcionalidad sin tener que hacer grandes cambios en el diseño.

Podemos tener, por ejemplo, un sistema muy fácil de extender, donde cada vez que un desarrollador introduzca un cambio tenga que esperar 2hs a que termine la compilación para poder verlo.

¿No es lo mismo que Testablity?
Testabilty se refiere a que tan fácil es verificar el software, por ejemplo: si es posible realizar test automatizados con facilidad o no.

Y lo que planteo es en cierto sentido similar, pero con objetivos levemente distintos: un sistema puede ser fácil de testear en el sentido que es posible automatizar tests u obtener información de estado relevante para el testing y aún así ser un tremendo "dolor" para desarrollar.

Un ejemplo: Mejorando el "developability" de Microwiki
Mi pequeño proyecto: microwiki; del que les conte hace un par de posts atras. Utiliza templates de html para renderizar las paginas web. Estos templates son resources y se cargan del classpath.

Por default Jetty no hace un reload del classpath cada vez que algo cambia -principalmente por que estar viendo el filesystem por cambios es una operación costosa.

Al principio tener que reiniciar microwiki para ver los cambios en un template no me molestaba: el proyecto es chico y reiniciar es rápido.

Sin embargo cuando empece a mejorar un poco el diseño del HTML esos segundos de presionar restart en el IDE y volver a cargar la página se volvieron una molestia.

Por suerte para no meterme con cuestiones de classpath reload y configuración de Jetty, algunos astros de "mantenibilidad" y "testeabilidad" se alinearon.

Para permitir que microwiki sea configurable mediante archivos de configuración, y que la interpretación de estos me sea fácil de mantener utilice un DSL de Groovy.
Es decir los archivos de configuración son código fuente Groovy, y en ellos es posible definir un template de la siguiente manera:

templates {
    display = '/dir/disp.html'
    edit = '/other/edit.html'
}

Por otro lado para facilitar el testing los templates implementan la interfaz: ViewTemplate. Esto me facilita la creación de mocks en los tests.

Juntando estas dos cosas, el cambio para facilitar el "developability" fue muy sencillo:

Agregue un método al builder que interpreta el DSL del archivo de configuración (ConfigBuilder):

ViewTemplate debug(source, Map context = [:]) {
  { Map ctx -> template(source, context).applyWith(ctx) } as ViewTemplate
}

Con este método es posible definir la configuración de esta manera:

templates {
 File prjDir = new File('/Users/diegof/Projects/microwiki/src/main/resources')

 edit = debug(new File(prjDir, 'microwiki/templates/edit.html'))
 search = debug(new File(prjDir, 'microwiki/templates/search.html'))
}

Y ya no hay que preocuparse por el reload: microwiki va a leer el archivo cada vez, y los cambios se ven de forma instantánea (claro que esto no tiene en cuenta los cambios en el stylesheet, aunque es un avance).

De que sirve esto... cuando tengan uno de esos proyectos web que cargan millones de cosas al inicio, es bueno dedicar un poco de tiempo a pensar como mejorar el "developability": aunque los appserver pueden hacer reload de ciertas cosas, no pueden hacer magia, y a veces pensar un poco en esta característica como parte de la calidad de la solución les va a ahorrar horas de frustración.

13 diciembre 2011

Configuración basada en Java con Spring 3.x

Si vienen siguiendo mi blog sabrán que además de postear una vez cada mil años, escribi algunas cosas sobre la dificultad de generar una configuración de Spring usando solo Java.

La buena noticia es que a partir de Spring 3.x se puede configurar el application context casi 100% en Java (hay una pequeña parte que todavia hay que hacer con el XML). Me entere de esto leyendo el anuncio del release 3.1 en InfoQ (y para mi sorpresa esta posibilidad ya existía en el release 3.0).

Si planean usar Spring 3.x es una buena noticia por que pueden tirar ese applicationContext.xml al tacho y hacerlo directamente en Java que es más fácil de mantener y refactorizar :)

La implementación se basa mucho en annotations, pero es lo suficientemente flexible como para permitir refactorings (para ejemplos ver la sección 3.11.3 del manual de Spring 3.0). Aunque hubiese preferido que lo hagan como en Grails, donde hicieron un builder usando Groovy (que dicho sea de paso, si están en un proyecto Java/Groovy pueden usar el builder sin necesidad de depender de otras librerías de Grails).

09 diciembre 2011

Mapeos Objeto/Relacional

Llegué a la conclusión de que los frameworks de mapeo Objeto/Relacional no sirven.

- Ehh! ¿Por que? Si yo uso Hibernate y esta bueno!

Aca van mis razones...

De Objetos...
Aprendí con el tiempo que la principal ventaja de la programación orientada a objetos es la posibilidad de tratar con distintas implementaciones a traves de su interfaz. 

Si estas pensando que me refiero al polimorfismo, tenes razón, pero di esta vuelta para evitar la tipica asociación de polimorfismo con jerarquias de clases.

Veamos un ejemplo que suele darse en los cursos introductorios: la cuenta bancaria.


Supongamos que tenemos en nuestro sistema un Cajero que puede hacer depositos en estas cuentas:



La instancia cuenta se pudo haber definido mediante una sub-clase, clase anonima, proxy dinamico o usando AspectJ y manipulación de bytecode. No importa. Mientras cumpla con la interfaz CuentaBancaria nuestro Cajero va a funcionar.

... y Relacional
Ahora supongamos que definimos una Cuenta de la siguiente manera:

CuentaBancaria cuenta = new Decorator(cuentaOriginal);

Nuestro Cajero sigue funcionando pero...

motorDeMapeoOR.guardar(cuenta);

explota.

El motor de mapeo O/R necesita saber como tratar la instancia para poder mapear su clase y sus variables de instancia a la base de datos. En otras palabras: encapsulamiento y polimorfismo las pelotas.

Quizas pienses que me fui a un ejemplo extremo y que uno nunca tiene casos como estos. 

Sin embargo en todos los sistemas JEE que veo siempre hay un "modelo" que termina estando sumamente acoplado a la base de datos:
  • No es conveniente jugar con las jerarquias de clases por que no se pueden mapear de una forma buena.
  • No se puede refactorizar algo para cambiar su composición por que es engorroso modificar los mapeos.
  • No se pueden usar objetos más "dinamicos", como objetos que tengan closures (en un lenguaje que lo permita), implemetaciones de interfaces definidas inline o simplemente un decorator como el del ejemplo.
Entonces la solución es: hacemos objetos pobres casi sin comportamiento solo para transferir datos (DTOs). Luego tenemos un modelo mas "rico" que se crea a partir de estos DTOs. 

Suena simple, pero en la práctica termina siendo una engorrosa y aburridisima repetición de código que los programadores tienden a solucionar de forma sencilla: en lugar de separar estos DTOs del modelo de dominio, terminan juntandolo en un modelo de dominio que solo guarda datos y muchas "clases utilitarias". Es decir aprovechan muy poco las posibilidades de polimorfimo que brinda el lenguaje. 

Pero si iba a estar todo tan acoplado y dependiente de la base de datos, ¿Para que nos preocupamos en hacer el mapeo? ¿No era mejor usar algo tipo Active Record? Al fin de cuentas son lo mismo que los DTOs pero con menos código.

Esto es lo que hace Rails y la primera vez que lo vi me parecio una cagada. Huele mal, pero los mapeos O/R no huelen mucho mejor.

...a las conclusiones
Los frameworks de mapeo O/R no cumplen con su promesa de un modelo de dominio libre de la base de datos, y me pregunto cuales son las ventajas en usarlos. Es decir si los usamos solo para facilitar la interacción con JDBC, quizás hay otras maneras que no requieran ningún tipo de mapeo y mantenimiento de "beans" intermedios.

En el caso de Java, creo que si Sun en lugar de definir una interfaz tan fea como la de RowSet que mezcla un montón de responsabilidades, hubiese hecho algo mas sencillo que se pueda usar como un Active Record, no habria razones para utilizar mapeos O/R como JPA.