Publicaciones y suscripciones

Sidebar 4.5

Porcentaje completado

En este capítulo:

  • Entenderemos cómo funcionan las publicaciones y las suscripciones.
  • Aprenderemos qué hace el paquete autopublish.
  • Veremos algunos ejemplos de patrones de diseño de publicaciones.
  • Las publicaciones y las suscripciones son dos de los conceptos más importantes de Meteor, pero puede que sean difíciles de comprender si acabas de empezar.

    Esto ha acarreado una gran cantidad de malentendidos, como la creencia de que Meteor es inseguro, o que las aplicaciones no pueden manejar grandes cantidades de datos.

    La “magia” que hace Meteor es la razón más importante de que ocurra esto al principio. Aunque la magia es, en última instancia muy útil, puede ocultar lo que realmente está pasando entre bastidores (como suele pasar con la magia). Así que vamos a desenvolver las capas de dicha magia para tratar de entender lo que está pasando.

    Los viejos tiempos

    Pero primero, vamos a echar una mirada a los buenos tiempos allá por 2011, cuando todavía no existía Meteor. Digamos que estás haciendo una sencilla aplicación con Rails. Cuando un usuario llega a tu sitio, el cliente (es decir, su navegador) envía una solicitud a tu aplicación, que reside en el servidor.

    Lo primero que hace la App es averiguar qué datos necesita ver el usuario. Estos podrían ser la página 12 de resultados de búsqueda, la información del perfil de usuario de Mary, los 20 tweets más recientes de Bob, y así sucesivamente. Básicamente, podríamos verlo como un dependiente de una librería buscando por los pasillos el libro que has pedido.

    Una vez que tiene los datos correctos, el segundo trabajo de la aplicación es traducir esos datos a un formato HTML agradable y legible (o JSON en el caso de una API).

    En la metáfora de la librería, estaríamos envolviendo el libro y metiéndolo en una bolsa bonita. Esta es la “Vista”, del famoso modelo Model-View-Controller.

    Por último, la aplicación coge el código HTML y lo envía hacia el navegador. El trabajo de la aplicación ha terminado, y puede relajarse tomando una cerveza mientras espera la siguiente solicitud.

    ¿Cómo lo hace Meteor?

    Veamos lo que hace Meteor tan especial. Como hemos visto, la principal innovación de Meteor es que, mientras que una aplicación Rails solo vive en el servidor, una aplicación Meteor también incluye componentes que se ejecutarán en el cliente (el navegador).

    Enviar un subconjunto de la base de datos al cliente.
    Enviar un subconjunto de la base de datos al cliente.

    Así que lo que ocurre es que el empleado de la tienda, no solo encuentra el libro, sino que además te sigue a casa para leértelo por la noche (admitiremos que suena un poco raro).

    Esta arquitectura permite a Meteor hacer cosas muy interesantes, la más importante es lo que Meteor llama base de datos en todas partes. En pocas palabras, Meteor tomará un subconjunto de la base de datos y la copiará en el cliente.

    Esto tiene dos grandes implicaciones: la primera es que en lugar de enviar código HTML, una aplicación Meteor envía datos actuales en bruto al cliente y deja que el cliente se ocupe de ellos (data on the wire). Lo segundo es que serás capaz de acceder, e incluso modificar esos datos instantáneamente sin tener que esperar al servidor (latency compensation).

    Publicaciones

    Una base de datos de una aplicación puede contener decenas de miles de documentos, algunos de los cuales podrían ser privados o confidenciales. Así que, obviamente, por razones de seguridad y escalabilidad, no deberíamos sincronizar toda la base de datos en el cliente.

    Por lo tanto, vamos a necesitar una forma de decirle a Meteor qué subconjunto de los datos se pueden enviar al cliente, y lo podremos hacer utilizando las publicaciones.

    Volvamos a Microscope. Aquí están todos los posts de nuestra aplicación que hay en la base de datos:

    Todos los posts que contiene la base de datos.
    Todos los posts que contiene la base de datos.

    Aunque esta función no exista realmente en Microscope, imaginemos que algunos de nuestros posts se han marcado como entradas con lenguaje abusivo. Aunque queramos mantenerlos en nuestra base de datos, no deben ponerse a disposición de los usuarios (es decir, enviarse a los clientes).

    Nuestra primera tarea será decirle a Meteor qué datos queremos enviar. Le diremos que solo queremos publicar posts sin marcar:

    Excluyendo posts marcados.
    Excluyendo posts marcados.

    Este sería el código correspondiente, que estaría en el servidor:

    // on the server
    Meteor.publish('posts', function() {
      return Posts.find({flagged: false});
    });
    

    Esto asegura que no hay manera posible de que el cliente pueda acceder a un post marcado. Esta es la forma de hacer una aplicación segura con Meteor: basta con asegurarse de que solo publicas los datos a los que el cliente actual debe tener acceso.

    DDP

    Puedes pensar en el sistema de publicaciones/suscripciones como un embudo que trasfiere datos desde una colección en el servidor (origen) a una en el cliente (destino).

    El protocolo que se habla dentro del embudo se llama DDP (que significa Protocolo de Datos Distribuidos). Para aprender más sobre DDP, puedes ver esta charla de la conferencia en Real-time de Matt DeBergalis (uno de los fundadores de Meteor), o este screencast de Chris Mather que te guía a través de este concepto con un poco más de detalle.

    Suscripciones

    A pesar de que no vamos a poner a disposición de los clientes los posts marcados, pueden quedar miles que no debemos enviar de una sola vez. Necesitamos una forma de que los clientes especifiquen qué subconjunto necesitan en un momento determinado, y aquí es exactamente donde entran las suscripciones.

    Cualquier dato que se suscribe, se refleja en el cliente gracias a Minimongo, la implementación de MongoDB en el lado del cliente que provee Meteor.

    Por ejemplo, digamos que estamos viendo la página del perfil de Bob Smith, y que solo queremos ver sus posts.

    La suscripción a los posts de Bob será un reflejo de ellos en el cliente.
    La suscripción a los posts de Bob será un reflejo de ellos en el cliente.

    En primer lugar, podríamos modificar nuestra publicación para que acepte un parámetro:

    // on the server
    Meteor.publish('posts', function(author) {
      return Posts.find({flagged: false, author: author});
    });
    

    Entonces podríamos definir ese parámetro cuando nos suscribimos a esa publicación desde el cliente:

    // on the client
    Meteor.subscribe('posts', 'bob-smith');
    

    Esta es la forma de hacer escalable una aplicación Meteor: en lugar de suscribirse a todos los datos disponibles, solo escogemos las piezas que necesitamos en un momento dado. De esta manera, evitaremos sobrecargar la memoria del navegador, sin importar si el tamaño de la base de datos del servidor es enorme.

    Búsquedas

    Ahora resulta que los mensajes de Bob tienen varias categorías (por ejemplo: “JavaScript”, “Ruby”, y “Python”). Tal vez todavía queremos cargar todos los Mensajes de Bob en la memoria, pero en este momento solo queremos mostrar los de la categoría “JavaScript”. Aquí es donde “la búsqueda” entra en juego

    Selección de un subconjunto de documentos en el cliente.
    Selección de un subconjunto de documentos en el cliente.

    Al igual que hicimos en el servidor, vamos a utilizar la función Posts.find () para seleccionar un subconjunto de estos datos:

    // on the client
    Template.posts.helpers({
      posts: function(){
        return Posts.find({author: 'bob-smith', category: 'JavaScript'});
      }
    });
    

    Ahora que tenemos una buena comprensión de cómo funcionan las publicaciones y suscripciones, vamos a profundizar un poco más, repasando los patrones de diseño más comunes.

    Autopublicación

    Si creas un proyecto Meteor desde cero (es decir, usando meteor create), el paquete autopublish se habilitará automáticamente. Como punto de partida, vamos a hablar acerca de lo que hace exactamente este paquete.

    El objetivo de autopublish es que sea muy fácil empezar a desarrollar y lo hace reflejando todos los datos del servidor en el cliente, lo que permite olvidarse de publicaciones y suscripciones y empezar directamente a escribir el código de la aplicación.

    Autopublish
    Autopublish

    ¿Y cómo funciona? Supón que tienes una colección en el servidor llamada 'posts'. Entonces autopublish buscará todos los posts que haya en la base de datos Mongo y los enviará automáticamente a una colección llamada 'posts' en el cliente.

    Así que si usas autopublish, no necesitas pensar en publicaciones. Los datos son omnipresentes, y todo es más sencillo. Por supuesto, aparecen problemas obvios al tener una copia completa de la base de datos en la caché de cada usuario.

    Por esta razón, autopublish solo es apropiado cuando estamos empezando, cuándo todavía no se ha pensado en las publicaciones.

    Publicando colecciones completas

    Si eliminamos autopublish, te darás cuenta de que todos los datos han desaparecido del cliente. Una forma fácil de traerlos de vuelta es, simplemente, replicar lo que hace autopublish publicando una colección en su totalidad. Por ejemplo:

    Meteor.publish('allPosts', function(){
      return Posts.find();
    });
    
    publicando una colección completa
    publicando una colección completa

    Todavía publicamos colecciones completas, pero al menos ahora tenemos control sobre qué colecciones publicamos. En este caso, publicamos la colección Posts pero no Comments.

    Publicando colecciones parciales

    El siguiente nivel de control es publicar solo una parte de una colección. Por ejemplo, solo los posts que pertenecen a un determinado autor:

    Meteor.publish('somePosts', function(){
      return Posts.find({'author':'Tom'});
    });
    
    Publicando una parte de una colección
    Publicando una parte de una colección

    Entre bastidores

    Si has leído la documentación de Meteor sobre publicaciones, tal vez te habrás sentido abrumado al oír hablar de added() y ready() para establecer los atributos de los registros en el cliente, y te habrá costado cuadrarlo con aplicaciones basadas en Meteor que hayas podido ver y que nunca usan esos métodos.

    La razón es que Meteor ofrece una comodidad muy importante: el método _publishCursor(). ¿Todavía no lo has utilizado? Tal vez no directamente, pero eso es exactamente lo que Meteor utiliza cuando devuelve un cursor (por ejemplo, Posts.find({'author':'Tom'})) desde una función publish.

    Cuando Meteor comprueba que la publicación somePosts ha devuelto un cursor, automáticamente llama a _publishCursor () para publicar ese cursor.

    Esto es lo que hace _publishCursor():

    • Se comprueba el nombre de la colección en el servidor.
    • Toma todos los documentos que coinciden con el cursor y los envía al cliente en una colección del mismo nombre. (Para hacer esto, utiliza .added()).
    • Cada vez que se añade, elimina o modifica un documento, envía esos cambios a la colección del lado del cliente. (Para hacerlo, utiliza .observe() en el cursor y .added(), .changed() y .removed()).

    Así, en el ejemplo anterior, podemos asegurar que el usuario solo tiene en la caché, los posts en los que está interesado (los escritos por Tom).

    Publicando propiedades parciales

    Hemos visto cómo publicar solo algunos de nuestros posts, pero ¡todavía podemos seguir recortando! Vamos a ver cómo publicar sólo algunas propiedades.

    Al igual que antes, vamos a utilizar find() para devolver un cursor, pero esta vez vamos a excluir ciertos campos:

    Meteor.publish('allPosts', function(){
      return Posts.find({}, {fields: {
        date: false
      }});
    });
    
    Publicando propiedades parciales
    Publicando propiedades parciales

    Por supuesto, también podemos combinar ambas técnicas. Por ejemplo, si quisiéramos devolver todos los posts de Tom, dejando de lado sus fechas, escribiríamos:

    Meteor.publish('allPosts', function(){
      return Posts.find({'author':'Tom'}, {fields: {
        date: false
      }});
    });
    

    Recapitulando

    Hemos visto cómo pasar de publicar todas las propiedades de todos los documentos de todas las colecciones (con autopublish) a publicar solo algunas propiedades de algunos documentos de algunas colecciones.

    Esto cubre los fundamentos de lo que se puede hacer con las publicaciones en Meteor, y estas técnicas tan sencillas deberían servir para la gran mayoría de casos de uso.

    Aún así, en ocasiones, tendrás que ir más allá combinando, vinculando, o fusionando publicaciones. ¡Todo esto lo veremos más adelante en uno de los capítulos del libro!