Desarrollo de UI con componentes - Manejo de una transacción dentro de un formulario de edición


Introducción

Si estamos modificando los datos de una entidad, es frecuente hacerlo en un formulario que tiene dos acciones posibles: aceptar o cancelar los cambios.


Es frecuente utilizar los homes persistentes como encargados del manejo de transacciones. En ese caso sería fácil predecir el comportamiento que tiene la ventana:
  • cuando el usuario presiona el botón Aceptar se envía un mensaje a la home para que actualice el objeto del formulario
  • caso contrario no hago ninguna llamada al home, entonces el home actúa como un centralizador de las acciones sobre un repositorio común para todos los usuarios que acceden a la aplicación.

Cuando cancelamos, al volver a la pantalla de búsqueda sincronizamos la información contra el mismo home, entonces volvemos a actualizar las referencias y cualquier cambio se pierde [1]. 

No obstante, debemos tener cuidado si el home maneja un conjunto de objetos en memoria, porque el binding del modelo de la pantalla de edición se hace contra la misma referencia que conoce el home en memoria:

     

Entonces cuando desde un textbox modificamos el valor del nombre, esto impacta directamente en las referencias de la vista y del home. Entonces al cancelar no podemos simplemente "ignorar" los cambios que ocurrieron, tenemos que cambiar la estrategia.

Opción 1: Manejo manual de los cambios

Una alternativa consiste en generar una copia del objeto original y asociarlo como modelo de nuestra ventana de modificación. 
  • al cancelar no necesitamos hacer nada, porque los cambios se hicieron contra un objeto que no está asociado al repositorio
  • al aceptar, pisamos el objeto original (referenciado desde el repositorio) con los nuevos atributos del objeto copia
De la misma manera podemos generar una copia y guardarla como atributo auxiliar de la vista. Nuestro modelo es el objeto al que apunta el repositorio. En ese caso
  • al aceptar le pasamos al home el modelo de la vista (que está modificado)
  • al cancelar tenemos que pisar el objeto modificado con la copia que resulta ser el objeto original. Esto vuelve atrás los cambios hechos.
       
  
Para aplicar esta estrategia el objeto de dominio tiene que ofrecer dos servicios:
  • clone: devuelve una copia del objeto original, implementando la interfaz Cloneable (y redefiniendo el método clone)
  • copyFrom: pisa los atributos de un objeto en base a la copia que recibimos como parámetro
El lector interesado puede consultar el ejemplo de los celulares que trabaja manualmente la transacción.

Opción 2: Utilizamos elementos transaccionales de Arena

Arena propone un esquema para no tener que resolver esto manualmente:
  • todos los objetos que sean modelos de una vista se anotan como @Transactional, para indicar a Arena que participa dentro de una transaccción. Esto incluye a los objetos de dominio y también a los objetos que modelan un caso de uso (que llamamos application model, se explican en el formulario de búsqueda).
    • como además queremos que disparen notificaciones a las vistas y demás interesados, se anotan como @TransactionalAndObservable
  • la vista en lugar de heredar de Dialog<T> hereda de TransactionalDialog<T>
Y con esto nos alcanza, Arena utiliza la vista para delimitar el alcance de una transacción: cuando el usuario presiona el botón Aceptar se finaliza (commit). En caso de error, o de presionar el botón Cancelar, la transacción se deshace (rollback), y los cambios se pierden.

El lector interesado puede consultar el ejemplo de los celulares que trabaja automáticamente la transacción.

[1] Si bien creemos que esa decisión es discutible es lo que pasa en la amplia mayoría de los sistemas que uno desarrolla y salirse de ese esquema es no sólo complicado sino controversial, por lo tanto vamos a adaptarnos a ello (para los interesados podemos ver otras propuestas en otro momento).

Comments