domingo, 31 de enero de 2010

Qi4j y Programación Orientada a Compuestos

Hace unas semanas un amigo me recomendó esta pagina de un nuevo framework Qi4j que se centra en el desarrollo del modelo de dominio y usa conceptos de AOP, DI (Dependency Injection) y DDD (Domain Driven Design).

El framework es una implementación de un nuevo paradigma de programación que es la Programación Orientada a Compuestos (Composites) (COP - por sus siglas en Ingles) usando Java 5 puro.

Los principios de la COP son:
- El comportamiento de un objeto depende del contexto.
- El desacoplamiento es una virtud.
- Las reglas de negocio importan mas.
- Las Clases han muerto, larga vida a las Interfaces.

Ellos argumentan que la Programación Orientada a Objetos (OOP) tiene ciertos problemas para realmente modelar el ciclo de vida de los objetos. Por ejemplo, un objeto no siempre tiene los mismos atributos y estos pueden variar (tener menos o mas atributos) en el tiempo, incluso el comportamiento de un objeto no es el mismo siempre ya que puede variar según el contexto donde se encuentre ejecutando.

Este tipo de cosas ha sido muy difícil implementarlas usando OOP y aunque se pueden implementar debería ser mas "natural".

A todos nos ha pasado que empezamos un sistema haciendo el análisis de los conceptos y reglas de negocio y buscamos hacer un diseño que represente y cumpla con todos esos requisitos lo mejor posible. Pero cuando llega la hora de codificar terminamos enfocando mas nuestros esfuerzos en la infraestructura y terminamos teniendo un modelo de dominio anémico (Martin Fowler explica esto aquí).

La Programación Orientada a Compuestos esta muy influenciada por el libro de Erick Evans (Domain Driven Design) donde propone conceptos y patrones de diseño para diseñar sistemas mas enfocados al modelo de dominio.

En la COP existen dos niveles de composición:

- Un nivel de composición para ensamblar objetos compuestos a partir de piezas mas pequeñas llamadas Fragmentos.
- Y un segundo nivel de composición para ensamblar Módulos a partir de compuestos (objetos del primer nivel de composición), Capas a partir de Módulos y terminar con una Aplicación compuesta de Capas.

Los Fragmentos a partir de los cuales se puede formar un objeto compuesto son:
- Mixins. Representan el estado de un objeto compuesto.
- Restricciones (Constraints). Reglas de validación tanto de entrada como de salida para los argumentos.
- Intereses (Concerns). Interceptan llamadas de métodos. Su uso se aplica generalmente a comportamientos que aplican a todo el sistema (cross-cutting behaviours).
- Efectos Secundarios (Side Effects). Se ejecutan después de la ejecución de un método pero no influyen en los datos de salida que entregan los métodos.

En fin, el framework Qi4j se ve muy interesante y vale la pena echarle un ojo para ver la forma en que trabaja.

Links de interés:

- Resumen del libro de Erick Evans DDD. http://www.infoq.com/minibooks/domain-driven-design-quickly

domingo, 10 de enero de 2010

Primeros Pasos con Spring Roo en Spring Source Tool Suite

Este ejemplo de primeros pasos esta hecho con el SpringSource Tool Suite, sin embargo, es posible hacerlo también desde la consola de Roo.

Vamos a crear una pequeña aplicacion de libreta de direcciones para este ejemplo.

Lo primero es crear el proyecto del tipo "Roo Project" con el nombre "PrimerosPasos" (el nombre puede ser de su eleccion solo recuerden que con ese nombre tendrá el contexto web).


Esto crea un proyecto con la estructura:



Para aquellos que usan maven se darán inmediatamente cuenta que es una estructura de un proyecto maven.

Para crear/configurar las cosas en Spring Roo se corren comandos desde la consola Roo, la cual desde el SpringSource Tool Suite se puede ejecutar con click derecho sobre el proyecto luego la opcion Spring Tools -> Open Roo Shell.

Ya en la vista de la consola Roo existe un campo de texto donde tecleamos los comandos que pueden ir creando el código de la aplicación. Si teclean "hint" el mismo Roo les dará pistas sobre que comandos teclear según lo ya creado dentro del proyecto.

Lo primero que hay que configurar el proveedor y la base de datos de la aplicación, y de hecho este es el primer punto que pide Roo crear si teclean "hint".


persistence setup --database MYSQL --provider HIBERNATE


Esto imprime como salida en la consola de roo:


roo> persistence setup --database MYSQL --provider HIBERNATE
Created SRC_MAIN_RESOURCES/META-INF/persistence.xml
Created SRC_MAIN_RESOURCES/META-INF/spring/database.properties
please enter your database details in src/main/resources/database.properties
Managed SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml


Dentro de los archivos que fueron creados esta el "database.properties" en el cual hay que configurar los parámetros para conectarnos a nuestra base de datos en mysql.

Ya configurados los parametros para conectarnos a la base de datos es necesario empezar a crear las entidades de nuestra aplicacion. Dado que nuestra aplicacion sera una libreta de direcciones empezaremos con la entidad "Persona".

Una vez mas podemos teclear "hint" en la consola de roo para que nos ayude.

Para crear una entidad hay que teclear el comando "entity" el cual recibe un argumento obligatorio y es el nombre de la clase de la entidad que vamos a crear.


entity --class com.jabaddon.roo.libretadirecciones.dominio.Persona


Sin embargo el comando "entity" puede recibir mas argumentos los cuales nos puede mostrar la consola de roo si tecleamos "--" y luego CRTL+SPACE.

Una vez ejecutado el comando "entity" esta es la salida en la consola de roo:


roo> entity --class com.jabaddon.roo.libretadirecciones.dominio.Persona
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona.java
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona_Roo_Entity.aj
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona_Roo_ToString.aj
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona_Roo_Configurable.aj


Notaran que la única clase java creada fue "Persona.java" y que si abren el código solo verán lo siguiente:


@Entity
@RooJavaBean
@RooToString
@RooEntity
public class Persona {
}


Los otros archivos "*.aj" son Aspectos agregados a la clase Persona.

En este punto si volvemos a teclear "hint" en la consola de roo nos dará consejos para crearle atributos a nuestra clase.

Agregaremos el atributo nombre a nuestra clase Persona ejecutando el siguientes comando en la consola:


field string --fieldName nombre



roo> field string --fieldName nombre
Managed SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona.java
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona_Roo_JavaBean.aj
Managed SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona_Roo_ToString.aj


Ok, en este punto del ejemplo me gustaría recalcar algo y para lograrlo primero hay que crear una clase de prueba de la clase Persona con el siguiente código:


package com.jabaddon.roo.libretadirecciones.dominio;

import org.apache.log4j.Logger;
import org.junit.Test;

public class PersonaTest {

private static final Logger LOGGER = Logger.getLogger(PersonaTest.class);

@Test
public void imprimirNombrePersona() {
Persona persona = new Persona();
persona.setNombre("Jimmy");
LOGGER.debug("El nombre es = " + persona.getNombre());
}
}


Lo siguiente es crear un archivo "log4j.propeties" en src/test/resources y configurarlo para que imprima el nivel debug de la siguiente forma: (si quieren pueden copiar el log4j.properties que se genero en src/main/resources/META-INF/spring/log4j.properties, pero hay que cambiarle el nivel a debug).


log4j.rootLogger=debug, consola

log4j.appender.consola=org.apache.log4j.ConsoleAppender
log4j.appender.consola.layout=org.apache.log4j.PatternLayout
log4j.appender.consola.layout.ConversionPattern=%d [%t] %-5p %c - %m%n


Una vez creada la prueba unitaria y el archivo log4j.properties, hay que ejecutar las pruebas del proyecto dando click derecho sobre el archivo pom.xml y ejecutar la opcion "Run As -> Maven Test". Con esto ejecutaremos las pruebas unitarias del proyecto y la salida de ejecutar esta acción es la siguiente (o pueden ejectuar las pruebas corriendo el comando "mvn test" en una consola normal):


...
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.jabaddon.roo.libretadirecciones.dominio.PersonaTest
2010-01-10 21:52:35,441 [main] DEBUG org.springframework.beans.factory.wiring.BeanConfigurerSupport - BeanFactory has not been set on BeanConfigurerSupport: Make sure this configurer runs in a Spring container. Unable to configure bean of type [com.jabaddon.roo.libretadirecciones.dominio.Persona]. Proceeding without injection.
2010-01-10 21:52:35,444 [main] DEBUG com.jabaddon.roo.libretadirecciones.dominio.PersonaTest - El nombre es = Jimmy
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.931 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
...


Ok, todo fue bien el nombre Jimmy se imprimió bien. Ahora miremos de nuevo el código de la clase Persona.java:


package com.jabaddon.roo.libretadirecciones.dominio;

import javax.persistence.Entity;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.tostring.RooToString;
import org.springframework.roo.addon.entity.RooEntity;

@Entity
@RooJavaBean
@RooToString
@RooEntity
public class Persona {

private String nombre;
}


Alguien puede explicarme porque al crear la prueba unitaria la clase Persona contenía los métodos setNombre y getNombre si el código java no lo tiene?!??!?!?!. La respuesta son dos palabras: Aspectos y Addons.

Recuerdan que al crear la entidad Persona Spring Roo creo unos archivos .aj? estos son los Aspectos. Vía aspectos y anotaciones Spring Roo agrega funcionalidad a las clases. Estos aspectos agregados por anotaciones a las clases son los llamados "Addons".

Los "Addons" son una característica que propone Spring Roo y es muy interesante ya que así podemos agregar funcionalidad a las clases de acuerdo a las necesidades y circunstancias. En algun post siguiente en el blog veremos mas a detalle acerca de los aspectos y addons de Spring Roo incluso para crear uno propio, por ahora continuemos con el ejemplo.

Ok, nuestra clase Persona ya tienen un atributo y es el nombre. Ahora vamos a agregarle otros atributos como: apellido y numero telefonico con los comandos siguientes:

                            
field string --fieldName apellido
field string --fieldName numeroTelefonico


Ahora crearemos la parte web de la aplicación creando los controllers. Para que roo nos ayude hay que teclean "hint controllers" en la consola de roo.

Para crear un controller que contenga las operaciones clasicas de altas, bajas y modificaciones (CRUD) ejecutamos el comando:


controller scaffold --class com.jabaddon.roo.libretadirecciones.web.PersonaController --entity com.jabaddon.roo.libretadirecciones.dominio.Persona


La salida de la ejecucion de este comando es la siguiente:


roo> controller scaffold --class com.jabaddon.roo.libretadirecciones.web.PersonaController --entity com.jabaddon.roo.libretadirecciones.dominio.Persona
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/web
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/web/PersonaController.java
Created SRC_MAIN_WEBAPP/WEB-INF/spring
Created SRC_MAIN_WEBAPP/WEB-INF/spring/webmvc-config.xml
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/web/PersonaController_Roo_Controller.aj
Created SRC_MAIN_WEBAPP/images
Created SRC_MAIN_WEBAPP/images/nl.png
Created SRC_MAIN_WEBAPP/images/update.png
Created SRC_MAIN_WEBAPP/images/es.png
Created SRC_MAIN_WEBAPP/images/delete.png
Created SRC_MAIN_WEBAPP/images/add.png
Created SRC_MAIN_WEBAPP/images/resultset_previous.png
Created SRC_MAIN_WEBAPP/images/resultset_next.png
Created SRC_MAIN_WEBAPP/images/favicon.ico
Created SRC_MAIN_WEBAPP/images/banner-graphic.png
Created SRC_MAIN_WEBAPP/images/resultset_last.png
Created SRC_MAIN_WEBAPP/images/gb.png
Created SRC_MAIN_WEBAPP/images/springsource-logo.png
Created SRC_MAIN_WEBAPP/images/it.png
Created SRC_MAIN_WEBAPP/images/show.png
Created SRC_MAIN_WEBAPP/images/sv.png
Created SRC_MAIN_WEBAPP/images/list.png
Created SRC_MAIN_WEBAPP/images/resultset_first.png
Created SRC_MAIN_WEBAPP/images/de.png
Created SRC_MAIN_WEBAPP/styles
Created SRC_MAIN_WEBAPP/styles/standard.css
Created SRC_MAIN_WEBAPP/styles/alt.css
Created SRC_MAIN_WEBAPP/WEB-INF/classes
Created SRC_MAIN_WEBAPP/WEB-INF/classes/standard.properties
Created SRC_MAIN_WEBAPP/WEB-INF/classes/alt.properties
Created SRC_MAIN_WEBAPP/WEB-INF/layouts
Created SRC_MAIN_WEBAPP/WEB-INF/layouts/default.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/layouts/layouts.xml
Created SRC_MAIN_WEBAPP/WEB-INF/views
Created SRC_MAIN_WEBAPP/WEB-INF/views/views.xml
Created SRC_MAIN_WEBAPP/WEB-INF/views/resourceNotFound.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/index.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/uncaughtException.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/dataAccessFailure.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/controller-index.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/tags
Created SRC_MAIN_WEBAPP/WEB-INF/tags/language.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/tags/theme.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/tags/pagination.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/i18n
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_sv.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_nl.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_it.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_es.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_de.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona/list.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona/show.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona/create.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona/update.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona/views.xml
Created SRC_MAIN_WEBAPP/WEB-INF/urlrewrite.xml
Created SRC_MAIN_WEBAPP/WEB-INF/web.xml
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml


Como verán, Spring Roo ha creado todos los archivos necesarios para tener una aplicación básica de altas, bajas y modificaciones sobre nuestra entidad Persona.

Para correr la aplicación bien podemos dar click derecho sobre el proyecto de Spring Source Tool Suite y darle "Run As -> Run on Server" o en una consola normal "mvn jetty:run".

Una vez que ha levantado el servidor vayan a la direccion http://localhost:8080/PrimerosPasos y listo!

Notaran que en la base de datos ya existe una tabla llamada "persona" con los atributos iguales a los que tiene nuestra entidad Persona.

NOTA: Yo tuve problemas ejecutando este ejemplo con Java 1.5 en una Mac pero todo corrió bien con Java 1.6.