Arena - Application model. Extendiendo el MVC.

Introducción

En Arena cada vista requiere un modelo. Esto implica encontrar una abstracción que pueda cumplir esa responsabilidad. Algunas pantallas pueden utilizar un objeto de dominio como modelo (ej: en el ejemplo de las apuestas), aunque en algunos casos es necesario modificar el modelo de algunos controles. 

Pero cuando la complejidad de la interacción con el usuario crece, no nos alcanza con tratar de resolverlo con un objeto de dominio como Celular, Socio o Película. 

Entonces nuestro objetivo es tener un objeto que sea totalmente independiente de la tecnología, pero que tenga todo el comportamiento necesario de la aplicación. Es la representación del comportamiento global de la aplicación sin la componente tecnológica. 

Algunos ejemplos

Una pantalla de búsqueda de clientes de una compañía que vende celulares:
Una ventana para crear un pedido, que selecciona cliente y producto (y también permite darlos de alta):


E incluso podemos pensar que una pantalla de alta o edición puede manejarse con un application model, esto permite no tener una referencia extra para conocer al home o repositorio:


Objetivo

Consideramos que es más importante la separación entre los componentes de la aplicación que dependen de la tecnología (vista, controller) y los que no (modelos de aplicación o de dominio); en lugar de la separación entre los roles (dominio vs. el resto, vista vs. controlador). Y el application model nos da la herramienta para lograr eso.

Esta estrategia nos permite:
  • Tener un modelo rico, en el cual poder programar y diseñar libremente, sin dependencias tecnológicas.
  • Por ser independientes de la vista pueden ser testeados unitariamente con herramientas sencillas (las que ya conocen, sin la complejidad de las herramientas de testeo automático para interfaces de usuario, vean para eso los tests del ejemplo de los celulares).
  • Por adaptarse a las necesidades de la vista, simplifican el mapeo que realizan los controllers.
El application model funciona como buffer entre la vista y el modelo de dominio y nos va a permitir construir esa parte de la aplicación programando con objetos y en nuestro lenguaje de preferencia, en lugar de tener que adaptarse a un framework, tecnología o lenguaje que no tiene la misma potencia. Algunas variantes de ese concepto se pueden ver en el artículo formas de vincular una vista con el modelo de dominio.

Objeto de dominio vs. application model

Podemos encontrar dos diferencias importantes:
  • El application model tiene otra naturaleza en cuanto a la forma en que aparece, no está (a priori) entre los conceptos que maneja el usuario.
  • Tiene otro ciclo de vida. Los objetos del dominio se crean, se guardan (utilizando probablemente un repositorio persistente), luego se pueden consultar, modificar, etc. En cambio los objetos de aplicación se usan una sola vez, suelen tener el estado conversacional entre un usuario y la aplicación, representan un caso de uso.

Esquema MMVC

El objeto Application Model da origen al esquema MMVC [1]:
  • V - Vista: la pantalla *Window
  • C - Controller: adapta la vista con el modelo, en este caso con el application model, es el encargado de resolver el binding
  • M - Modelo de la vista/modelo de aplicación/application model: modela al caso de uso, mantiene simple el binding con la vista
  • M - Modelo de dominio

Tanto la vista como el controller son componentes que dependen de la tecnología utilizada. Por el contrario el application model y el modelo de dominio no tienen que ver con la tecnología y son componentes que podríamos aprovechar si quisiéramos cambiar de tecnología de presentación. 

[1] Otras variantes similares son MVVM (otro link), MVB.

Implementaciones

  • para pantallas de búsqueda, podemos tener 
    • búsquedas "by example", en ese caso el application model es
      • una instancia de SearchByExample<T>, donde T es un objeto de dominio (celular, socio del videoclub, alumno, etc.) que extiende Entity
      • una subclase de SearchByExample<T>. T es un objeto de dominio aunque no tiene restricciones adicionales
    • si la búsqueda es común
      • posiblemente nos convenga abstraer en un objeto aparte el criterio de búsqueda
      • podemos heredar de Search<T>
      • o podemos modelar nuestro propio "buscador" (como en el caso del ejemplo de los celulares)
  • para pantallas donde no hay búsqueda o donde así lo decidamos, debemos generar nosotros nuestra propia implementación
Todos los application model deben anotarse con @Observable.
También es conveniente que implementen la interfaz Serializable.

Search by example

¿Qué es un "search by example"?

Es hacer una búsqueda a partir de un ejemplo o prototipo: supongamos que existe un objeto de dominio Socio que representa a un socio de un videoclub y que tiene como atributos el nombre, una dirección y si está activo (un valor booleano).
  • queremos buscar los socios cuyos nombres comiencen con "Lopez". Entonces construimos un objeto Socio que nos sirve como prototipo o example y le seteamos en el nombre el valor "Lopez". El atributo dirección y activo quedan en null.
  • también funciona para buscar los socios activos, basta con definir en el example un socio con el atributo activo en true (sin ningún otro atributo cargado)
  • las búsquedas pueden combinarse, por lo general tratando de cumplir todas las condiciones escritas en el example. Si en el nombre seteamos "Lopez" y en direccion "Alvarez" vamos a pedir que ambas condiciones (la de nombre y dirección) se cumplan, pero eso depende de cómo configuremos la búsqueda by example.
SearchByExample tiene como objetivo generar búsquedas partiendo de un ejemplo de tipo T. Por eso sus responsabilidades son:
  • void search(): disparar la búsqueda
  • void clear(): blanquear el example
  • void removeSelected(): eliminar el elemento de la colección (persistente o no, eso depende del home)
Para instanciar un search by example, basta con pasarle un Repositorio o DAO, como en el ejemplo del videoclub:

>>AlquilarPeliculas - application model del caso de uso Alquilar películas
public AlquilarPeliculas(Socio socio) {
    // instancia otro application model
    this.searchPeliculas = new SearchByExample<Pelicula>(new PeliculaDaoColeccionImpl());

Search

La clase Search es abstracta, y obliga a quien la subclasifique a definir tres métodos:
  • List<T> doSearch(): la que resuelve efectivamente la búsqueda (ya que buscar es un template method)
  • void clear()
  • void removeSelected()

Otros casos

En cualquier otro caso, quien implemente el application model sólo debe definir como interfaz lo que el caso de uso requiera.

Buenas prácticas


Cuando diseñes una aplicación, no trates de utilizar el mismo application model para todas las ventanas. Un application model no es el modelo para toda la aplicación, sino que sirve a un único caso de uso particular.


Consejos
  • Para evitar que un application model se transforme en un God object: si dos pantallas comparten el mismo application model, revisá que haya información verdaderamente compartida, que no haya variables utilizadas únicamente en una pantalla u otra (bad smell asociado: Divergent Change). En resumen, no es recomendable reutilizar el application model entre vistas, a menos que requieran bindings similares. Por lo general, cada vista maneja su propio modelo asociado.
  • No definas el application model como un Singleton compartido entre todos los usuarios. Si abstrae un caso de uso es un objeto que no puede compartirse entre distintas sesiones.
  • Una buena forma de validar qué tan simple o complejo es el diseño de un application model es construirle tests unitarios automatizados
  • El application model no debería tomar responsabilidades de otros objetos (repo, ni objetos de dominio). Esto implica que debe saber delegar convenientemente al objeto que corresponda.
  • Recordá que no es conveniente que el application model conozca a los elementos de la vista, para poder reutilizarlo en diferentes tecnologías de UI (además de tener la posibilidad de testearlo en forma unitaria)
Comments