Publications et Souscriptions

Aparté 4.5

Traduction complétée à

Au programme de ce chapitre :

  • Comprendre comment les publications et les souscriptions fonctionnent.
  • Apprendre le comportement par défaut du paquet autopublish.
  • Voir quelques exemples de patrons de publications.
  • Publications et souscriptions sont un des concepts les plus importants et fondamentaux dans Meteor, mais peut être difficile à se représenter quand on débute.

    Cela a conduit à beaucoup d'incompréhensions, telles que Meteor n'est pas sécurisé, ou que les applications Meteor ne peuvent pas gérer une large quantité de données.

    Une grande part de la raison pour laquelle les gens trouvent ces concepts un peu perturbants est la “magie” que Meteor fait pour nous. Bien que cette magie est d'une aide considérable, elle peut obscurcir ce qui se passe vraiment en arrière-plan (comme la magie a tendance à le faire). Détaillons un peu les couches de magie pour essayer de comprendre ce qu'il se passe.

    Les vieux jours

    Mais dans un premier temps, regardons en arrière dans les vieux jours de 2011 quand Meteor n'était pas encore là. Disons que vous développez une simple application Rails. Quand un utilisateur atteint votre site, le client (i.e. votre navigateur) envoie une requête à votre application, qui est en cours sur votre serveur.

    Le premier travail de l'application est de trouver quelles données l'utilisateur a besoin de voir. Ça pourrait être à la page 12 de vos résultats, les informations de profil de Marie, les 20 derniers tweets de Bob, etc. Vous pouvez imaginer un marchand de livres recherchant dans des allées le livre que vous avez demandé.

    Une fois que les bonnes données ont été sélectionnées, le second travail de l'application est de traduire ces données dans un joli HTML lisible par un humain (ou en JSON dans le cas d'une API).

    Dans la métaphore du marchand de livre, vous pouvez envelopper le livre que vous venez juste d'acheter et le mettre dans un joli sac. C'est la partie “vue” du fameux modèle MVC (Model-View-Controller).

    Finalement, l'application prend du code HTML et l'envoie au navigateur. Le travail de l'application est terminé, et maintenant que toutes ces choses sont sorties de ses mains virtuelles, elle peut se détendre avec une bière et attendre la prochaine requête.

    La vision Meteor

    Regardons ce qui rend Meteor si spécial en comparaison. Comme nous l'avons vu, l'innovation clé de Meteor est que là ou l'application Rails vit seulement sur le serveur, une application Meteor inclut également un composant côté client qui va s'exécuter sur le client (le navigateur).

    Envoyer un sous-ensemble de la base de données au client.
    Envoyer un sous-ensemble de la base de données au client.

    C'est comme un marchand qui non seulement vous trouve le livre, mais aussi vous suit jusque chez vous pour vous le lire au moment de dormir (ce qui peut paraître un peu bizarre à entendre).

    Cette architecture laisse Meteor faire des choses sympathiques, parmi lesquelles la principale que Meteor appelle database everywhere. Simplement, Meteor va prendre un sous-ensemble de votre base de données et le copier sur le client.

    Cela implique deux idées importantes : premièrement, au lieu d'envoyer du code HTML au client, une application Meteor va envoyer la donnée brute, véritablement, et laisser le client se débrouiller avec ça (data on the wire). Deuxièmement, vous allez pouvoir accéder aux données instantanément et même les modifier sans avoir à attendre un autre aller-retour vers le serveur (latency compensation).

    Publier

    Une base de données d'application peut contenir des dizaines de milliers de documents, lesquels peuvent être privés ou sensibles. Donc nous n'allons évidemment pas entièrement copier notre base de données sur le client, pour des raisons de sécurité et d'adaptabilité.

    Par conséquent nous allons avoir besoin d'une méthode pour dire à Meteor quel sous-ensemble de données peut être envoyé au client, et nous l'effectuerons à l'aide d'une publication.

    Revenons à Microscope. Nous avons ici tous les articles de l'application dans notre base de données :

    Tous les articles contenus dans la base de données.
    Tous les articles contenus dans la base de données.

    Bien que cette fonctionnalité n'est pas encore présente dans Microscope, nous allons imaginer que certains articles ont été signalés pour langage abusif. Bien que nous voulons les garder dans notre base de données, ils ne devront pas être rendus disponibles aux utilisateurs (i.e. envoyés au client).

    Notre première tâche sera de dire à Meteor quelles données nous voulons réellement envoyer au client. Nous allons dire à Meteor que nous voulons seulement publier les articles non signalés :

    Exclure les articles signalés.
    Exclure les articles signalés.

    Voici le code correspondant, qui résidera sur le serveur :

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

    Cela assure qu'il n'y a aucun moyen possible qu'un client soit capable d'accéder à un article signalé. C'est exactement comme ça que vous allez sécuriser une application Meteor : assurez-vous juste que vous publiez seulement les données que vous voulez fournir au client.

    DDP

    Fondamentalement, vous pouvez imaginer le système de publication/souscription comme un tunnel où transitent les données de la collection côté serveur (source) vers la collection côté client (cible).

    Le protocole qui est parlé dans ce tunnel est appelé DDP (Distributed Data Protocol). Pour en savoir plus sur DDP, vous pouvez visionner cette présentation de la Real-time Conference par Matt DeBergalis (un des créateurs de Meteor), ou cette capture vidéo par Chris Mather qui parcourt ce concept dans les moindres détails.

    Souscrire

    Quand bien même nous voulons rendre les articles non signalés disponibles pour les clients, nous ne pouvons pas envoyer des milliers d'articles en une fois. Nous avons besoin d'un moyen de spécifier aux clients quel sous-ensemble de données ils ont besoin à un moment donné, et c'est exactement là que les souscriptions interviennent.

    Les données auxquelles vous souscrivez seront dupliquées sur le client grâce à Minimongo, l'implémentation Meteor de MongoDB côté client.

    Par exemple, disons que nous sommes en train de naviguer sur la page de profil de Bob, et nous voulons seulement afficher ses articles.

    Souscrire aux articles de Bob les copiera sur le client.
    Souscrire aux articles de Bob les copiera sur le client.

    Premièrement, nous modifierons notre publication pour prendre un paramètre :

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

    Et nous définirons ensuite ce paramètre au moment de la souscription à cette publication dans le code côté client de notre application :

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

    C'est de cette façon qu'on rend une application Meteor adaptable côté client : au lieu de souscrire à toutes les données disponibles, on choisit les parties dont nous avons actuellement besoin. De cette façon, vous allez éviter les surcharges mémoire du navigateur quelle que soit la taille de la base de données côté serveur.

    Trouver

    Maintenant qu'il est possible d'étiqueter les articles de Bob dans plusieurs catégories (par exemple “Javascript”, “Ruby” et “Python”), on pourrait avoir envie de charger tous les articles en mémoire mais de n'afficher que ceux de la catégorie “Javascript”. C'est dans ce cas de figure que “trouver” intervient.

    Sélectionner un sous-ensemble sur le client.
    Sélectionner un sous-ensemble sur le client.

    Comme nous l'avons fait sur le serveur, nous utiliserons la fonction Posts.find() pour sélectionner un sous-ensemble de nos données.

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

    Maintenant que nous avons une bonne idée de quel rôle jouent les publications et les souscriptions, creusons un peu plus profond et observons quelques patrons d'implémentations.

    Autopublish

    Si vous créez un projet Meteor à partir de rien (i.e. en utilisant meteor create), il aura automatiquement le paquet autopublish activé. Commençons par discuter de son rôle.

    Le but de autopublish est de rendre très facile le début de développement de votre application Meteor, et ce en dupliquant automatiquement toutes les données du serveur vers le client, ainsi en prenant soin des publications et des souscriptions pour vous.

    Autopublish
    Autopublish

    Comment ça fonctionne ? Supposez que vous avez une collection appelée 'posts' sur le serveur. Alors autopublish enverra automatiquement chaque article qu'il trouvera dans la collection posts Mongo vers une collection appelées 'posts' sur le client (en considérant qu'il y en a un).

    Donc si vous utilisez autopublish, vous n'avez pas besoin de penser aux publications. Les données sont omniprésentes, et les choses sont simples. Bien sûr, il y a des problèmes évidents d'avoir une copie complète de la base de données de l'application en cache sur chaque machine d'utilisateur.

    Pour cette raison, autopublish est seulement approprié quand vous démarrez, et que vous n'avez pas encore réfléchi aux publications.

    Publier des collections complètes

    Une fois que vous avez supprimé autopublish, vous allez rapidement vous rendre compte que toutes vos données ont disparues du client. Une façon simple de revenir et simplement de copier ce que autopublish fait, et de publier une collection dans sa totalité. Par exemple :

    Meteor.publish('allPosts', function(){
      return Posts.find();
    });
    
    Publier une collection complète
    Publier une collection complète

    Nous publions encore des collections complètes, mais au moins nous avons maintenant le contrôle sur quelles collections nous publions ou pas. Dans ce cas, nous publions la collection Posts mais pas Comments.

    Publier des collections partielles

    Le niveau de contrôle suivant est de publier seulement une partie d'une collection. Par exemple seuls les articles qui appartiennent à un certain auteur :

    Meteor.publish('somePosts', function(){
      return Posts.find({'author':'Tom'});
    });
    
    Publier une collection partiellement
    Publier une collection partiellement

    En coulisses

    Si vous avez lu la documentation de publication Meteor, vous avez peut-être été dépassé par l'utilisation de added() et ready() pour appliquer les attributs des enregistrements sur le client, et pour recoller ça avec les applications Meteor que vous avez déjà vu et qui n'utilise pas ces méthodes.

    La raison est que Meteor fournit une méthode vraiment très pratique : _publishCursor(). Vous n'avez jamais vu ça utilisé ? Peut-être pas directement, mais si vous retournez un curseur(i.e. Posts.find({'author':'Tom'})) dans une fonction de publication, c'est exactement ce qu'utilise Meteor.

    Quand Meteor voit que la publication somePosts a retourné un curseur, il appelle _publishCursor() pour – vous l'avez deviné – publier ce curseur automatiquement.

    Voici ce que _publishCursor() fait :

    • Il vérifie le nom de la collection côté serveur.
    • Il prend tous les documents correspondants du curseur et envoie celui-ci dans une collection côté client du même nom. (Il utilise .added() pour faire ça).
    • A chaque moment qu'un document est ajouté, supprimé ou modifié, il envoie ces changements à la collection côté-client. (Il utilise .observe() sur le curseur et .added(), .changed() et removed() pour faire ça).

    Dans l'exemple au-dessus, nous pouvons nous assurer que l'utilisateur a seulement les articles qui l'interessent (ceux écrit par Tom) disponibles dans leur cache côté client.

    Publier des propriétés partielles

    Nous avons vu comment publier seulement certains articles, mais nous pouvons continuer à affiner ! Voyons comment publier seulement certaines propriétés spécifiques.

    Juste comme avant, nous allons utiliser find() pour retourner un curseur, mais cette fois nous allons exclure certains champs :

    Meteor.publish('allPosts', function(){
      return Posts.find({}, {fields: {
        date: false
      }});
    });
    
    Publier des propriétés partiellement
    Publier des propriétés partiellement

    Bien sûr, nous pouvons également combiner deux techniques. Par exemple, si nous voulions retourner tous les articles de Tom en laissant de côté leurs dates, nous écririons :

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

    Résumé

    Donc nous avons vu comment publier chaque propriété de tous les documents de chaque collection (avec autopublish) jusqu'à publier seulement certaines propriétés de certains documents de certaines collections.

    Ceci couvre les bases de ce que vous pouvez faire avec les publications Meteor, et ces simples techniques s'occuperont de la vaste majorité des cas d'utilisation.

    Parfois, vous aurez besoin d'aller plus loin en combinant, reliant, assemblant des publications. Nous allons en discuter dans un prochain chapitre !