Trabajando con persistencia (mal uso de los DAOs)
Todo programador Java sabe lo que es un DAO (
Data Access Object).
Todo programador Java los utiliza, en practicamente todas las aplicaciones que desarrolla y requieren el acceso a la base de datos.
Todo programador Java esta convencido de que las "buenas prácticas" de programación incitan a usar una capa de DAOs para realizar el acceso a la persistencia de la aplicación.
El uso de este patrón de los "core J2EE patterns" está tan difundido que nadie se pregunta si es correcto o no usarlo: simplemente, hay que tenerlo en una aplicación "seria".
Bueno, yo creo que todo esto es false. Es más, creo que en la mayor parte de los casos el uso del patrón DAO no hace sino agregar complejidad innecesaria a la aplicación.
¡Alto! Por favor, no me prendan fuego ni me griten "hereje" todavía... esperen a escuchar mi historia.
¿Que es el patrón DAO?El patrón DAO tiene su orígen en los primeros días de J2EE, cuando las condiciones de desarrollo de software era bastante diferente a la actual. Tiene como objetivo ofrecer una capa de abstracción entre la lógica de acceso a datos
de fuentes diversas. La idea del patrón es la composicion del modelo lógico a partir de estas fuentes, de forma que el origen de los datos sea transparente a la manipulación de los mismos en la aplicación.
Ok, no hay nada que decir en contra de esto, es una situación descrita en la cual dicha abstracción me vendría de perlas, veamos un ejemplo: supongamos que tengo una base de datos relacional que guarda datos de clientes de un banco, pero con datos de las cuentas de los mismos que residen en un sistema legacy X. Una aplicacion tipo requiere la relación Cliente->Cuentas.
En este caso, es perfectamente entendible el uso de un DAO (que funciona como una Factory de objetos Cliente) para, en el método ClienteDao.get(Id)", obtener una primer versión del cliente desde la base relacional y componer el resto con los datos necesarios provenientes del sistema Legacy.
Pero... ¡el problema es que no es así como se usa!Hoy la "capa de Dao" es de uso obligatorio en una aplicación Java... aún cuando no hay ninguna necesidad de integración con ningún sistema legacy, y hay una entrada única de datos (lo cual es así en el 99% de los sistemas): Una aplicación java, en general, tendrá un único orígen de datos (una base de datos relacional, normalmente, aunque en muchos casos esto sea totalmente innecesario, pero eso es para otro momento).
Además, con el auge de los ORM (
Object Relational Mappers) como Hibernate, se hace por demás estpupido agregar una capa de objetos entre el modelo de negocio y la base de datos. Una capa que, por demás, si esta bien hecha, lo único que hará será llamar a métodos "save", "remove" o "find", todos iguales entre sí.
Mucho más fácil parece ser seguir otro camino:
- Un único singleton para toda la aplicación que realiza las operaciones de acceso a los datos (Hibernate implementa un EntityManager que puede ser utilizado con estos fines).
- Definicion de las consultas necesarias a través de las características que proveen los ORMs. En JEE5 e hibernate esta la annotation @NamedQuery para definir los mismos en el mismo objeto que mapea.
- En caso absolutamente necesario, el uso del patrón "method factory" parece más apropiado que el patrón Dao.
Por ejemplo, así quedaría una definición de clase usando esta forma de acceso:
@Table
@NamedQuery("aliveGalaxy", "alive=true")
public class Galaxy {
// Atributos
private String nombre;
private Boolean alive;
// etc.
// Necesito un query muy raro
public static List unQueryRaro() {
//Definicioon del query
return DataManager.getInstance().executeQuery(unQuery);
}
//Resto de la clase
}
Lo que conseguimos en este caso es concentrar todo lo que es referido al objeto "Galaxia" en su definición (como corresponde en una buena programación orientada a objetos), mientras que usando DAOs quedaba distribuido en dos lugares distintos.
Las consecuencias son obvias:
- Reducimos la cantidad de código, o como mínimo lo agrupamos mejor.
- Mejoramos la claridad del código: yo sé que todo lo referente a una galaxia, esta en Galaxia.
- Mejoramos la mantenibilidad, dado que todos los cambios necesarios se concentran en un único punto.
Para finalizar, los detractores de esta solución podrán decir que de esta forma no se obtiene una "buena separacion entre capas" (argumento que he escuchado mucho, sino demasiado, en mis años de programador). Lo único que puedo decir al respecto es que las capas son una abstracción analítica que ayudan a la organización de la aplicación. Pero en definitiva, hacemos "Programación Orientada a Objetos", no "Programación Orientada a Capas": al final, nuestro sistema debe ser un universo de objetos que colaboren de una forma clara y coherente. Y para eso, las capas ayudan, pero no siempre.
¿Hacer una aplicación comercial en Squeak? (Parte 1)
Antes que nada, un poco de contexto.
Mantenerse actualizado en un ambiente tan dinámico como el mundo Java es bastante complejo (no digo que sea el único ambiente complejo, sino que lo es… otros -como el mismo Squeak-, han demostrado serlo también). Para poder incorporar en proyectos comerciales novedades tecnológicas es preciso tenerlas bien probadas, así no se le hace pagar al cliente los costos de una investigación o una implementación fallida. Para hacer eso, a veces ocupo tiempo de mi trabajo (cuando no estoy asignado a proyectos), pero la mayor parte del tiempo investigo en mi casa, cuando estoy aburrido. Así, investigué frameworks que luego fueron incorporados en mi trabajo como estándares, como JSF y GWT.
El principal banco de pruebas con el que trabajo es un juego online que estoy desarrollando de a poco, juego de estrategia espacial, con algunas novedades con respecto a los cientos que ya pululan por la red.
Bien. En ese contexto es que estoy realizando mi “prueba” con Squeak.
Los parámetros de medición, siempre subjetivos, serán comparaciones entre los distintos elementos del juego, antes "GWT Planets" y ahora "Squeak Planets" que estoy haciendo.
Dicidí como primer paso migrar lo que ya tenía hecho en Java a Smalltalk. Primero pensé que sería un trabajo engorroso, sin embargo se demostró que no era cierto, hay muchas cosas que no necesito migrar, y que en Java “ensuciaban” mi código y en Smalltalk/Squeak no. A saber:
a) No necesito configuración (en Java lo tenía con Spring). Todos los objetos se configuran a si mismos, en métodos de objeto o de clase.
b) No necesito interfaces!
c) En java, la tendencia es a poner todos los métodos creacionales en “Abstract Factories”, que crean, buscan, etc. objetos del sistema.
En Smalltalk, la mayoría de veces no es necesario. Con aplicar el patrón “Method Factory” alcanza y sobra.
d) La persistencia: en Java, dado que necesito inicializar el sistema cada vez que vpoy a probar algo, necesito rápidamente ir al mapeo de datos (con Hibernate), para poder tener un set de datos de prueba.
En Smalltalk, no.
Como tengo un ambiente de objetos vivos, con tener el set de datos que necesito, alcanza. Por supuesto, en algún momento esto tendrá que resolverse… pero no ahora, cuando estoy desarrollando. No tengo que preocuparme por algo que en definitiva es secundario.
Con respecto a esto, en realidad ocurrió algo gracioso al principio: como estaba acostumbrado a la forma java de desarrollar, estaba en principio definiendo objetos con Ids, confundido en la forma de poder acceder a ellos. En realidad, no hay necesidad de esto, los ids son una consecuencia de la persistencia (y de la persistencia relacional en particular)… ya lo resolveré cuando lo necesite, por ahora, todo corre correctamente en la imagen, en donde tengo un universo de objetos fácilmente alcanzable.
e) No necesito capas. No hay que dividir el diseño en capas de: aplicación, lógica, acceso a datos, etc. con un dominio de objetos bien definidos alcanza.
Por supuesto, alguien dirá que es preciso establecer una división analítica, con lo cual estoy de acuerdo… al menos en algunos casos, no en todos.
Sin embargo, lo que es una división analítica, no tiene por qué ser física (por ejemplo, el patrón DAO, es una abstracción absolutamente innecesaria en el contexto de mapeos ORM… pero eso lo discutiré más adelante).
¿Parece poco?
Con estos cambios, mi universo de objetos se redujeron de más de 150 clases en la versión Java a poco más de 30 en Smalltalk.
Además del código compacto logrado, se gana en mantenibilidad dado que todo lo relacionado con el ciclo de vida de un objeto esta en un único lugar, no distribuido entre n capas, cada una de las cuales hace un pequeño segmento de lo necesario.
Y si hablamos de productividad, es cierto que gran parte del dominio lo tenía definido en la primer versión, ¡Pero tardé dos semanas en traducir un código que me costó más de seis meses en desarrollar!
En cuanto tuve estos elementos definidos, solo me faltaba empezar a realizar la interfaz de usuario… pero eso queda para el próximo post.
Java ya fue
Y... son demasiados problemas: que si el application server, que si las excepciones son unhandled, que si hay que poner una capa para aislarse de otra capa que se aisla de otra capa, que si esto lo tengo que hacer en jsp con taglibs, en jsf o tapestry y que pongo una capa entre el cliente y el servidor, y la configuro con xml, usando spring o lo que sea. Y si tengo que pensar en el acceso a datos y entonces necesito una capa de DAOs que me maneje el acceso a los mismos. Ni hablar de que el modelo de datos hay que mapearlo, con xdoclets o annotations y como estoy transformando una base relacional en un modelo de objetos tengo varias complicaciones extras; y ya que hice el mapeo y tengo un modelo, mejor hago unos controllers o managers para meter la logica de negocio, así no se me mezcla con los mapeos, aunque, claro, con eso deje en la practica de programar a objetos. O le pongo un "aspecto" que me maneje los logs o la auditoría, aunque con eso este no solo abandonando OOP (que no sería malo, si me diera una ventaja) sino que este volviendo, casi, casi, a los GOTOs que siempre dijeron que eran pésimos.
En fin, son demasiados problemas. Y todavía no empecé a hablar del problema real que tengo que resolver, por el cual me pagan.
¿Cuanto tiempo pasa además, con todo este quilombo a resolver antes de poder mostrarle al cliente algo en lo que pueda reconocer que estamos trabajando para resolver su problema?
Es evidente que hay que solucionar este quilombo o no vamos a lograr nunca tazas de productividad razonables.
Lo primero que uno pensaría es: ¿Y si hacemos las cosas en .Net?
El problema es que .Net tiene el mismo tipo de concepto. Y aunque haya menos fwks a escoger, de cualquier forma son muchas cosas, y muchas capas.
Podemos, entonces, avanzar un poco estandarizando la arquitecura de una aplicación java: limitamos las posibilidades, pero ganamos en startup. Barbaro, avanzamos un poco. Pero el problema sigue estando, sigo teniendo que manejar un monton de cosas, antes de poder resolver el problema de negocios.
En este momento, tenemos un panorama similar en ambos mundos, .Net y Java.
Y no estamos un ápice mas cerca de encontrar una solución.
A partir de la llegada de Java 6 (que incluye groovy en su especificación) y del hecho de que lenguajes dinámicos como ruby o python han ganado algo de "momento" me hice la pregunta: ¿Y si es el momento de empezar a pensar en el próximo paso?
¿Y cual sería este próximo paso?
Bien, evidentemente, el universo de problemas que quiero resolver me indica para donde tengo que empezar a pensar... los lenguajes dinámicos permiten un acceso más rápido a la construcción y, bien usados, permiten encarar algunos de estos problemas fácilmente.
Como todo nuevo comienzo es una vuelta a las raices, terminé con el primer lenguaje dinámico existente, y con el primer lenguaje realmente orientado a objetos: Smalltalk.
Y tomé Squeak y empecé a jugar... no se puede tener una opinión concluyente, pero creo que voy a empezar a escribir sobre la experiencia al respecto, sobre todo porque hay muchos prejuicios preestablecidos que es preciso comabatir, o corroborar (no se puede hacer algo realmente productivo con esto, es uno de ellos), pero también porque trabajar con algo de forma "fresca" puede permitir aprender fortalezas y debilidades, que pueden llegar a aplicarse en otros ambientes.
En fin... veremos que sale.