Busqueda de peliculas REST - Parte Server


Objetivo

El segundo ejemplo necesita un servicio REST que funcione del lado Server. Para ello contamos con dos opciones similares
  • un Controller XTrest
  • un Controller en Grails
en cualquiera de las dos opciones implementaremos un método http GET que reciba un parámetro de búsqueda (el título de la película) y devolveremos la lista de películas que contengan el valor ingresado. El formato de salida será un JSON.

Servicio Grails

Aquellos que vieron la tecnología Grails conocerán el ejemplo del alquiler de películas, donde
  • el usuario escribe parte del título de una película
  • y como el campo tiene autocompletado automático, le van apareciendo las primeras n películas cuyo título contiene el texto que escribió
Esta funcionalidad está implementada con Ajax, que dispara los pedidos en forma asincrónica desde el cliente (en el browser) hacia el servidor. El pedido se hace por http y la respuesta vuelve en formato JSON.

No obstante, el output que tiene el servidor está pensado para el control autocomplete de jQuery-UI:

  1. [{
  2. "label": "Jardines en otoño",
  3. "value": "Jardines en otoño",
  4. "id": 36
  5. }, {
  6. "label": "El jardinero",
  7. "value": "El jardinero",
  8. "id": 40
  9. }, {
  10. "label": "Michael Jackson : 30 Aniversario MSG",
  11. "value": "Michael Jackson : 30 Aniversario MSG",
  12. "id": 55
  13. },

Vamos a crear un nuevo método que devuelva una representación más cercana al objeto película y a su género:

  1. [{
  2. "titulo": "Jardines en otoño",
  3. "actores": "Séverin Blanchet, Pascal Vincent, Muriel Motte, Michel Piccoli, Lily Lavina",
  4. "genero": {
  5. "id": 1,
  6. "descripcion": "Drama"
  7. }
  8. }, {
  9. "titulo": "El jardinero",
  10. "actores": "Daniel Auteuil, Jean Pierre Darroussin, Fanny Cottençon, Alexia Barlier, Hiam Abbass",
  11. "genero": {
  12. "id": 2,
  13. "descripcion": "Comedia"
  14. }
  15. }, {
  16. "titulo": "Michael Jackson : 30 Aniversario MSG",
  17. "actores": "Michael Jackson",
  18. "genero": {
  19. "id": 3,
  20. "descripcion": "Musical"
  21. }
  22. }, {

La implementación es sencilla, el home aplica el filtro y devuelve una colección de películas, mientras que el controller adapta la lista de películas y pasa un mapa que termina transformándose en un DTO de facto:

class PeliculasController {

    def repoPeliculas = HomePeliculas.instance

    def getPeliculas(String tituloContiene) {
        render repoPeliculas.getPeliculas(tituloContiene, 10).collect { pelicula ->
            [titulo: pelicula.titulo,
             actores: pelicula.actores,
             genero: [
                id: pelicula.genero.id,
                descripcion: pelicula.genero.descripcion
                 ]
            ]
        } as JSON
    }

PeliculasController.groovy - Grails


DTO

¿Por qué directamente no pasar el objeto película?

  • el campo sinopsis (el resumen de una película) es largo. Si estamos transmitiendo varias películas, esto tiene un costo de pasarlo por la red y luego de recibirlo en el cliente. Además ¿qué pasa si el cliente sólo quiere conocer el título, los actores y el género y si un objeto película tiene muchos atributos más? Hay una tensión entre armar un servicio general para todos los que necesiten usarlo y la especificidad para aprovechar al máximo el ancho de banda.
  • el otro tema asociado es que el objeto género necesita un id y una descripción, y no matchea exactamente con la salida por default:

  1. "genero": {
  2. "class": "ar.edu.videoclub.domain.Genero",
  3. "descripcion": "Drama"
  4. },

Por eso aparece como respuesta una abstracción: el DTO o Data Transfer Object, que es el objeto que modela la transferencia entre el cliente y el servidor:

  • como el DTO se concentra en el envío de información de un nodo a otro, no suele tener comportamiento
  • introduce redundancia de conceptos (Pelicula y PeliculaDTO apuntan a representar la misma entidad)
  • la ventaja de tecnologías como Grails es que el mapa reemplaza la necesidad de generar una clase adicional ad-hoc (que sólo se usa en el contexto de un request), aunque hay que pensar en técnicas de reutilización si necesitamos transferir la misma información en más de un lugar
  • por otra parte, cuando trabajamos con sistemas distribuidos necesariamente aparecen redundancias: tanto en el cliente como en el servidor tengo películas, pero no necesariamente coinciden
data transfer object as the communication medium between those layers

Servicio en XTrest

La implementación en XTrest es muy similar:
  • esperamos el pedido vía http GET
  • dentro de la URL el último valor es el parámetro títuloContiene
  • el controller delega la búsqueda a un objeto repositorio con un máximo de n películas
  • y convierte esa lista a JSON a partir del framework Jackson (utilizando un ObjectMapper en el método toJson), esto también evita construir un objeto DTO específico, reemplazándolo por annotations del lado del objeto de dominio.

    @Get("/videoclub-ui-grails-homes-xtend/peliculas/:tituloContiene")
    def Result buscarPeliculas() {
        ok(VideoClub.getInstance.buscar(tituloContiene).toJson)
    }
PeliculasController.xtend (XTrest)

Tip: al redefinir el Path como /videoclub-ui-grails-homes-xtend/peliculas... podemos utilizar tanto nuestra app Grails como Xtend sin que sufra cambios el cliente Android.

Tarea para el lector

Comparando ambas soluciones, vemos que en Grails el controller genera el JSON manualmente vs. esa tarea en XTrest se resuelve mediante annotations de objetos de dominio. Analice comparativamente ambas implementaciones buscando ventajas y desventajas de cada una.
Comments