Tema 3: Patrones de diseño para aplicaciones distribuidas con EJBs

En este tema se tratarán los patrones de diseño que pueden utilizarse en aplicaciones con arquitectura distribuida y/o que hacen uso de EJBs. La introducción de los EJBs locales en la versión 2.0 de la especificación cambió el panorama ya que anteriormente ambas ideas venían asociadas de manera indisoluble. Aunque esto ya no es así, trataremos los EJBs y las aplicaciones remotas en el mismo tema, dando ideas básicas de cuándo y cómo utilizar EJBs locales o remotos.

Como se verá la mayor parte de los patrones que se empleaban en aplicaciones locales siguen empleándose en aplicaciones remotas, aunque la diferencia fundamental es la razón por la que se usan: en aplicaciones remotas, la fuerza básica que hay detrás del uso de muchos de los patrones es minimizar los costes de las comunicaciones. En aplicaciones locales tienden a utilizarse los mismos patrones para desacoplar las capas desde el punto de vista lógico.

Como los patrones son muy similares a los ya vistos, en este tema los describiremos de manera más breve, dando únicamente las diferencias que hayan en cuanto a la finalidad o la forma de utilizar los anteriores.

2.1. Patrones para la capa de integración

El uso de EJBs de entidad con persistencia manejada por el contenedor simplifica considerablemente la implementación de la capa de integración y elimina la necesidad de patrones de diseño del tipo Domain Store que vimos en el tema anterior. Unicamente será necesario implementar algún tipo de persistencia manual si el CMP se queda pequeño para nuestras necesidades, y aun en ese caso se puede acudir a APIs como JDO para obtener persistencia automática.

No obstante, queda pendiente la cuestión de la eficiencia, ya que los beans de entidad son objetos "pesados" no adecuados para ciertos usos. Por ejemplo supongamos que los productos de nuestro catálogo se modelan como beans de entidad. El trabajo extra en que incurre el servidor de aplicaciones para gestionar los EJBs estará justificado para ciertas operaciones (por ejemplo, introducción de nuevos productos o modificación de datos) pero no en otras (búsqueda de productos por parte de un posible comprador, que exige la creación de gran cantidad de EJBs que además solo leen pero no escriben, con lo que en realidad no se necesitaban transacciones ni control de concurrencia). Una especie de "truco" para solventar este problema es utilizar acceso convencional a la base de datos mediante JDBC para las operaciones de solo lectura. Este "truco" se formaliza en el patrón Fast Lane Reader.

2.1.1. Acelerando el acceso a datos de solo lectura: Fast Lane Reader

Problema

Supongamos que el modelo de objetos del dominio está basado en beans de entidad. Ciertas operaciones, como las de lectura de un gran número de datos que no van a ser modificados, van a ser poco eficientes. Queremos acelerar las operaciones de solo lectura, aun a costa de que sean datos no totalmente actualizados.

Aplicabilidad

Solución

Aplicar el patrón Fast Lane Reader (también llamado JDBC for reading), que consiste simplemente en utilizar una versión simplificada de un Data Access Object que utilizaremos cuando hagamos operaciones que queramos que sean especialmente eficientes. Dicho Data Access Object implementará únicamente operaciones de lectura de la base de datos, a diferencia de los DAO que se utilizan en aplicaciones sin EJBs y que también son responsables de la inserción y borrado de datos.

Consecuencias

La principal consecuencia positiva es el aumento de la eficiencia. La negativa es que el cliente puede recibir datos que no están actualizados (cosa que no ocurriría con EJBs de entidad).

2.2. Patrones para la capa de negocio


Hasta la aparición de la especificación 2.0 el uso de EJBs venía inevitablemente asociado al acceso remoto y por tanto a problemas de eficiencia. En base a intentar paliar este problema se desarrolló un modelo para el trabajo con EJBs que se ha convertido en el "modelo clásico" recomendado por la mayoría de desarrolladores (incluyendo a la propia Sun) y ampliamente utilizado. Este modelo se sintetiza en el patrón más importante de aplicaciones remotas con EJBs: el Session Facade. Este es una versión con EJBs del patrón que vimos en el tema anterior llamado Service Facade.

2.2.1. Minimizando las llamadas remotas: Session Facade

Problema

Supongamos que tenemos un modelo de dominio creado a base de beans de entidad. El acceso directo a los mismos por parte de los clientes sería problemático por varias razones:
Por tanto necesitamos algún mecanismo que nos permita: reducir la comunicación por la red, colocar la lógica de negocio de forma coherente y agrupar las operaciones en una única transacción declarativa.

Solución

Utilizar el patrón Session Facade.  En este, los EJBs de sesión forman el "frontal" de la capa de negocio, ofreciendo un API de métodos al que pueden acceder los clientes. Para realizar sus operaciones, los EJBs de sesión llaman a los de entidad a través de su interfaz local, pasando a la situación de la figura 1 (b), en la que se reduce el número de operaciones remotas. Los EJBs de entidad no son directamente accesibles por los clientes para evitar los problemas descritos anteriormente.

Independientemente de la mejora de eficiencia, puede verse que este patrón es una implementación mediante un EJB de sesión del  Service Facade, que se usa también en aplicaciones sin EJBs para centralizar la lógica de negocio. Aquí cumple también el mismo papel, ofreciendo una capa con la lógica de negocio que implica a varios objetos.

Un problema es cuántos Session Facades hay que definir en una aplicación. Lo más sencillo es definir uno solo, que contendría todo el API de la capa de negocio, pero este enfoque lleva a clases con demasiado código no relacionado entre sí. La práctica común es agrupar los casos de uso relacionados (que actúan sobre las mismas entidades) en un solo Session Facade.

Consecuencias

Entre las positivas:
Como consecuencias negativas, un uso inadecuado del patrón puede llevar a:

2.2.2 Transfiriendo datos a los clientes: Data Transfer Object

Problema

Como ya hemos visto, una de las fuerzas fundamentales en una aplicación distribuida es reducir al mínimo las llamadas remotas. No obstante en una aplicación distribuida en algún momento habrá que transferir datos de una capa a otra. Buscamos la manera menos costosa de transferir información entre capas.

Solución

Realizar la transferencia de información entre capas empaquetada en objetos del tipo DTO, Data Transfer Objects, que en su versión más sencilla son simplemente JavaBeans. Las operaciones serán mucho más eficientes con una única llamada que transfiera un paquete de información en lugar de con múltiples llamadas con datos individuales. Como se recordará, los DTOs también se usaban en aplicaciones locales pero en ese caso su uso era por cuestiones de organización más que de eficiencia.

Con la versión 1.X de EJB era común la recomendación de usar DTOs para que los Session Facades se comunicaran con los beans de entidad, lo que era lógico ya que eran llamadas remotas. El uso de interfaces locales hace innecesario el empleo de los DTOs en este contexto y los limita a la transferencia de información entre capa de presentación y de negocio.

No nos extendemos en las consecuencias del uso del patrón, ya que son las mismas que en el caso de aplicaciones locales.

2.2.3. El patrón "caído en desgracia": Composite Entity


Con la versión 1.X de EJB se recomendaba que el modelo de dominio no estuviera compuesto completamente de EJBs de entidad, sino que únicamente se usaran para objetos "independientes" (por ejemplo, en caso de tener un objeto Usuario y otro TarjetaCredito, el primero sería un EJB de entidad pero el segundo un POJO, ya que no tiene sentido si no está asociado a un usuario). Este es el patrón llamado Composite Entity. Esto se hacía para evitar las llamadas entre beans de entidad, remotas siempre en EJB 1.X. Si en la actualidad se usan interfaces locales, es posible hacer un modelo completo del dominio con EJBs de entidad sin incurrir en excesivas penalizaciones de eficiencia. La única penalización consistiría en los chequeos de transacciones y seguridad que efectuara el contenedor, pero al parecer las implementaciones actuales no suelen realizar estos chequeos si no se especifican en el descriptor de despliegue. La seguridad y transaccionalidad la puede controlar el Session Facade de una sola vez en toda la operación.

Desaparecido en gran parte el problema de eficiencia, en la actualidad, la mayoría de expertos parecen no recomendar el uso de Composite Entities, pero Core J2EE Patterns sigue recomendándolo y discutiéndolo ampliamente. Para ayudar a decidir, recogemos aquí las consecuencias positivas y negativas de su uso.

Consecuencias

Entre las positivas:
Entre las negativas:

2.2.4. Localizando EJBs: Service Locator

El patrón Service Locator tiene el mismo uso que en aplicaciones locales: la localización de recursos. Unicamente hay que tener en cuenta que en aplicaciones con EJBs adquiere también la responsabilidad de localizarlos vía JNDI y cachear las referencias.

2.2.5. Haciendo transparentes los EJBs de negocio: Business Delegate

Al igual que en el patrón anterior, la principal razón de uso del Business Delegate es exactamente la misma que en aplicaciones locales: ocultar la implementación de la capa de negocio. En aplicaciones distribuidas hay que considerar además dónde reside físicamente el Business Delegate. Aunque desde el punto de vista lógico pertenece a la capa de negocio (y como tal deben ocuparse de él los desarrolladores de esta capa), desde el punto de vista físico se debe colocar en la capa de presentación para que las comunicaciones con los objetos de esta última sean locales. Los clientes que solicitan sus servicios no tienen por qué saber que estos son accedidos en realidad de forma remota. Esto abre una posible consecuencia negativa para el mal uso del patrón: que los desarrolladores de la capa de presentación olviden que los métodos de negocio son en realidad remotos y acaben haciendo un uso ineficiente y poco optimizado de las llamadas.

2.3. La arquitectura de Pet Store

Pet Store es la aplicación de ejemplo J2EE más conocida. Consiste en una hipotética tienda de animales on-line desarrollada por Sun con la idea de proporcionar un ejemplo realista que sirviera como guía en el buen uso de las tecnologías J2EE 1.3. En su momento fue objeto de polémica por parte de Microsoft, que afirmó haber creado su propia versión de PetStore en .NET que era del orden de 28 veces más rápida. El contraataque del mundo J2EE estuvo fundamentalmente basado en que PetStore no era una aplicación que pretendiera estar altamente optimizada (por ejemplo en el uso de SQL), sino ilustrar buenas prácticas de diseño J2EE. No obstante toda la polémica interesada por parte de Microsoft, sí cabe pensar que PetStore es un escaparate de tecnologías J2EE que se podrían haber simplificado de haber sido una aplicación real y no un ejemplo de uso. Aun así, constituye un ejemplo interesante de cómo y por qué usar patrones de diseño en una aplicación J2EE distribuida, por lo que resumiremos aquí la arquitectura de alguno de sus módulos. Aunque la aplicación tiene módulos para procesamiento de órdenes de compra, administración, compra de productos, etc, aquí veremos solo lo que es la tienda de "comercio electrónico".

2.3.1. La arquitectura del módulo Web

La aplicación web de Pet Store tiene las opciones típicas de cualquier sitio de comercio virtual:
La arquitectura se ha dividido en distintos módulos, cada uno encargándose de un grupo de tareas relacionadas (figura 2)


Figura 2: módulos de la aplicación web de Pet Store

Módulo de control

Recibe las peticiones del usuario, controla la navegación por las páginas y desencadena las acciones correspondientes, delegando su ejecución a otros módulos. En la arquitectura de este módulo hay que destacar los siguientes puntos:

Módulo de registro y login de usuarios

Se encarga de que el usuario pueda hacer login en la aplicación, registrarse si lo desea y sea redirigido a la página de login si hay alguna operación que lo requiera (por ejemplo, comprar). En la arquitectura de este módulo hay que destacar:

Módulo de catálogo

Permite al usuario navegar por el catálogo de productos. A grandes rasgos, tiene los siguientes requerimientos:
Vistos estos requerimientos las principales decisiones de diseño son (figura 3)

Figura 3: Diagrama de clases del módulo de catálogo

Módulo de clientes

Controla y gestiona todos los datos de clientes, permitiendo altas, bajas y modificaciones, y relacionándolos con los usuarios que hacen login en la aplicación.

La principal decisión de diseño en este módulo es modelar los datos del cliente (perfil, cuenta, información de contacto, tarjeta,...) como un conjunto de EJBs locales de entidad con persistencia manejada por el contenedor. Esto se puede hacer así porque aporta los beneficios de los EJBs con una arquitectura local.


Figura 4: Diagrama de clases del módulo de clientes

Módulo de carro de compra

Los requerimientos del módulo son los siguientes:
La principal decisión de diseño viene dada por el hecho de que la información es propia de cada usuario, lo cual deja la alternativa de mantener la información en la sesión HTTP o bien en un EJB de sesión con estado. En este caso se utiliza la última opción para que todo tipo de clientes puedan acceder al carro.


Figura 5: Diagrama de clases del módulo de carro de la compra