martes, 1 de abril de 2008

Cairngorm II

En el catálogo de patrones J2EE los llamaríamos TransferObjects.

Cuando empezamos el desarrollo de una aplicación, la toma de requerimientos puede llevarnos a tomar la decisión de consumir datos dinámicos a partir de XML’s, Web Services, DataObjects a través de RPC o cualquier otro sistema existente.

En una fase posterior del desarrollo puede darse el caso en que esto tenga que cambiar. Imaginemos el caso en que se decide utilizar SOAP como sistema de consumo de datos. Conforme la aplicación va creciendo y tiene que dimensionarse podemos experimentar problemas de rendimiento o una mala eficiencia del formato. Por ello deberíamos cambiar toda la capa de consumo de datos.

Dependiendo de cómo esté diseñada la arquitectura de nuestra aplicación este cambio podría tener un gran impacto y afectar no sólo al consumo de datos. Siguiendo con el ejemplo anterior si la lógica de nuestra aplicación utiliza objetos específicos arrastrados del modelo de datos, el cambio afectaría a dos capas. Esto es, si la lógica de nuestra aplicación trabaja sobre objetos XML puros en todas las capas sería realmente laborioso el cambiar la aplicación para que esta trabajara con objetos consumibles a través de RemoteObject.

Otro punto en contra de trabajar con objetos ceñidos a la capa de consumo es la limitación que podemos estar introduciendo a nuestro código. Sin ir más lejos, y aunque usemos e4x, un objeto parseado desde un XML no se puede castear a un objeto con identidad propia dentro de nuestra aplicación.

Si extrapolamos este problema a la parte servidora podríamos poner como ejemplo el cambio de motor de base de datos. Si inicialmente optamos por trabajar con una BBDD Access pero después por motivos de performance decidimos migrar a Oracle el cambio debería afectar sólo a la capa de consumo de datos y no a la lógica de mi aplicación. Si en mi aplicación he arrastrado a todas las capas objetos Recordset específicos de Access la migración será extensa ya que se perdería la compatibilidad.

Con el fin que un cambio en cualquiera de las capas (por profundo que sea) no afecte al resto de capas (ortogonalidad y baja cohesión de código) introducimos el concepto de Transfer Object o Value Object (en adelante VO’s). Este tipo de objetos suelen ser lightWeight y de una naturaleza muy simple. No tienen ningún tipo de business logic y son simples contenedores de datos estructurales (mapeo orientado a objetos).

Al tratarse de objetos transversales a todas las capas un cambio en la capa generadora de estos objetos no implica un cambio en las capas consumidoras y viceversa, consiguiendo así la ortogonalidad deseada.

Pongamos el ejemplo de una aplicación de lectura de noticias (sea el Pod de la home de madeinflex). La primera fase de toma de requerimientos nos lleva a adoptar la solución de consumir dichas noticias directamente desde xmls siguiendo el formato de RSS. En una iteración posterior se decide consumir noticias desde unos WebServices (por ejmplo mxna). El problema es que en el plano implementacional la estructura de datos que devuelven los SOAP no se parece en nada a la estructura de las RSS, aunque en el plano conceptual ambos modelos de datos son igualmente noticias.

La solución para evitar este tipo de problemas pasa por utilizar VO’s. Conceptualmente podemos ver una noticia como un objeto con tres campos: title, excerpt y link. En un plano implementacional podríamos definir este objeto como una clase: ItemVO

Dependiendo de la fuente de datos este objeto se construirá de una forma u otra. En caso de consumir datos de un xml tendremos en alguna parte de nuestro código una función que sepa traducir desde un XML con una DTD concreta a nuestro objeto agnóstico al formato. Si consumimos los datos de un WebService, esta conversión se realizará siguiendo unas reglas distintas que si consumimos los datos a través de RemoteObject, etc.

Desde un punto de vista implementacional un VO puede estar constituido de propiedades simples como las del ejemplo anterior, pero también podría estar definido con getters y setters siguiendo más la forma de Java.

El que nuestra aplicación sólo use VO's no sólo nos beneficia en grandes aplicaciones sujetas al cambio, sinó que además canaliza nuestro conocimiento entre distintas aplicaciones y fomenta la reutilización de otras clases (no vinculadas a un formato de datos concreto como XML).

Paralelamente a la capa de consumo de datos, podemos hacer que las propiedades de nuestros VOs sean [Bindables]. Lo cual implica que cualquier consumidor/suscriptor de una propiedad de nuestro VO será notificado si ésta cambia.

Esta peculiaridad, y potencia, permite relacionar el modelo de datos con la capa de presentación (View). Si bindeamos una propiedad de un VO a un widget visual que permita su visualización, en el momento en el que desde cualquier otro punto de nuestra aplicación se altere el valor de dicha propiedad, inmediatamente la representación visual de dichos datos se actualizará.


El hecho que ItemVO esté marcado como [Bindable] a nivel de clase implica que todas sus propiedades lo sean. De esta forma cuando se modifica el valor de las propiedades los TextInputs que tienen la propiedad text bindeada a alguna de las propiedades del VO, reflejan los cambios de inmediato.

Cairngorm trabaja de forma extensa con el concepto de VO, potenciando su uso. Como convenio se debería hacer que cada uno de nuestros VO’s implementa la inteface com.adobe.cairngorm.vo.ValueObject con el fin de potenciar el polimorfismo si en algun momento lo necesitáramos.


Fte: http://www.madeinflex.com/2006/10/15/cairngorm-ii-value-objects/

No hay comentarios:

Publicar un comentario