-
Notifications
You must be signed in to change notification settings - Fork 14
/
04s-publications-and-subscriptions.md.erb
217 lines (134 loc) · 13.4 KB
/
04s-publications-and-subscriptions.md.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
---
title: Publicações e Subscrições
slug: publications-and-subscriptions
date: 0004/01/02
number: 4.5
sidebar: true
contents: Aprenda como publicações e subscrições funcionam.|Aprenda o que o pacote padrão Autopublish faz.|Veja mais alguns exemplos de padrões de publicações.
paragraphs: 52
---
Publicações e subscrições são uns dos mais importantes conceitos no Meteor, mas pode ser difícil você entender enquanto está começando.
Isso levou a uma série de mal-entendidos, como a crença de que o Meteor é inseguro, ou que as aplicações Meteor não podem lidar com grandes quantidades de dados.
Uma grande parcela do motivo das pessoas inicialmente acharem estes conceitos um pouco confusos é a "mágica" que o Meteor faz por nós. Embora essa mágica seja muito útil, ela pode obscurecer o que está de fato acontecendo por trás das cenas (o que uma mágica tende a fazer). Então vamos analisar as camadas dessa mágica e entender o que está acontecendo.
### Os Velhos Tempos
Mas primeiro, vamos olhar para os bons velhos tempos em 2011 quando o Meteor ainda não havia sido lançado. Vamos dizer que você está fazendo um simples aplicativo Rails. Quando um usuário entra no site, o cliente (i.e seu navegador) manda uma requisição para sua aplicação, que está vivendo no servidor.
O primeiro trabalho da aplicação é perceber qual dado o usuário precisa de visualizar. Isso pode ser a página 12 dos resultados da busca, informação do perfil de usuário da Mary, os últimos 20 tweets de Bob, e por ai vai. Você pode nisso como sendo, basicamente, um atendente de livraria navegando entre os corredores para encontrar o livro que você pediu.
Uma vez que o dado correto tenha sido selecionado, o segundo trabalho da aplicação é traduzir este dado em um belo e legível HTML (ou JSON no caso de uma API).
Na metáfora da livraria, isso seria embrulhar o livro que você comprou em uma embalagem e colocá-lo em uma bela bolsa. Esta é a parte "View" (visão/vista) do famoso modelo Model-View-Controller (Modelo-Visão-Controlador).
Finalmente, a aplicação pega o código HTML e manda para o navegador. O trabalho da aplicação é finalizado, e agora que tudo está fora de suas mãos virtuais você pode pegar uma cerveja enquanto espera pela próxima requisição.
### O Caminho do Meteor
Vamos rever o que faz o Meteor ser tão especial em comparação. Como vimos, a principal inovação do Meteor é que enquanto aplicações Rails estão vivas apenas **no servidor**, um aplicativo Meteor inclui um componente no lado do cliente que vai rodar **no cliente** (o navegador).
<%= diagram "client-server", "Colocando um subgrupo do banco de dados no cliente.", "pull-right" %>
Isso é como um balconista que não apenas encontra o livro pra você, mas também te segue até em casa e lê o livro para você à noite (fato que vamos admitir soar um pouco assustador).
Essa arquitetura permite ao Meteor fazer várias coisas legais, principalmente com o que o Meteor chama de [database everywhere](http://docs.meteor.com/#sevenprinciples) (banco de dados em todos os lugares). Simplesmente, Meteor vai pegar uma parte do seu banco de dados e *copiá-lo para o cliente*.
Isso tem duas grandes implicações: primeira, ao invés de mandar código HTML para o cliente, a aplicação Meteor vai mandar **o real, dado puro** e o cliente vai lidar com ele ([data on the wire](http://docs.meteor.com/#sevenprinciples) - dado na rede). Segundo, você vai ser capaz de **acessar o dado instantaneamente** sem ter que esperar por uma ida e volta do servidor ([latency compensation](http://docs.meteor.com/#sevenprinciples) - compensação de latência).
### Publicação
O banco de dados de um aplicativo pode conter dez mil documentos, sendo alguns destes privados ou sensíveis. Então obviamente não devemos simplesmente espelhar nosso banco de dados completamente no cliente, por razões de segurança e escabilidade.
Então vamos precisar de uma forma de dizer ao Meteor quais **subgrupos** de dados podem ser enviados ao cliente, e vamos realizar isso através de uma **publicação**.
Vamos voltar ao Microscope. Aqui estão todos os posts do no aplicativo situados no banco de dados:
<%= diagram "collections-1", "Todos os posts contidos no nosso banco de dados.", "pull-center" %>
Embora esse recurso reconhecidamente não exista no Microscope, vamos imaginar que alguns de nossos posts tenham sido marcados por linguagem abusiva. Mesmo que queiramos que eles continuem em nosso banco de dados, eles não devem estar disponíveis para os usuários (i.e enviados ao cliente).
Nossa primeira tarefa vai ser dizer ao Meteor qual dado nós **vamos** querer enviar ao cliente. Vamos dizer ao Meteor que desejamos **publicar** apenas os posts sem marcações:
<%= diagram "collections-2", "Excluindo posts marcados.", "pull-center" %>
Aqui temos o código correspondente, que deve estar no servidor:
~~~js
// no servidor
Meteor.publish('posts', function() {
return Posts.find({flagged: false});
});
~~~
Isso assegura para que **não exista uma forma** de um cliente estar apto a acessar post marcados. Isso é exatamente a forma que você torna uma aplicação Meteor segura: apenas assegure-se de publicar dados que você queira que estejam disponíveis para acesso no cliente.
<% note do %>
### DDP
Fundamentalmente, você pode pensar sobre o sistema de publicação/subscrição como um funil que transfere dados de uma coleção no lado do servidor para uma coleção no lado do cliente.
O protocolo que é utilizado neste funil é chamado **DDP** (que significa Distributed Data Protocol - Protocolo de Dados Distribuidos). Para aprender mais sobre DDP, você pode assistir [essa palestra da *Real-Time Conference*](http://2012.realtimeconf.com/video/matt-debergalis) por Matt DeBergalis (um dos fundadores do Meteor), ou [este screencast](http://www.eventedmind.com/posts/meteor-subscriptions-and-ddp) por Chris Mather que conduz você por este conceito com um pouco mais de detalhe.
<% end %>
### Subscrição
Mesmo que seja nossa intenção deixar qualquer publicação não marcada disponível para os clientes, nós não podemos simplesmente enviar milhares de publicações de uma vez. Nós precisamos de uma forma para os clientes especificarem qual subgrupo de dados eles precisam em um momento em particular, e é exatamente ai que as **subscrições** entram.
Todo dado que você se subscrever vai ser **espelhado** no cliente graças ao Minimongo, a implementação do MongoDB feita pelo Meteor no lado do cliente.
Por exemplo, vamos dizer que estamos atualmente navegando na página perfil de Bob Smith, e somente queremos mostrar **seus** posts.
<%= diagram "collections-3", "Subscrevendo nos posts de Bob irá espelhá-los no cliente .", "pull-center" %>
Primeiro, iríamos alterar nossa publicação para receber um parâmetro:
~~~js
// no servidor
Meteor.publish('posts', function(author) {
return Posts.find({flagged: false, author: author});
});
~~~
E podemos então definir este parâmetro quando nos *subscrevermos* nesta publicação no código do nosso aplicativo no lado do cliente:
~~~js
// no cliente
Meteor.subscribe('posts', 'bob-smith');
~~~
Assim é como torna uma aplicação Meteor escalável no lado do cliente: Ao invés de se subscrever para *todos* os dados disponíveis, você somente escolhe as partes que você precisa atualmente. Dessa forma, você vai evitar sobrecarga de memória no browser não importando o quão grande seja seu banco de dados no lado do servidor.
### Encontrando (finding)
Agora os posts de Bob devem ser espalhados em mútiplas categorias (por exemplo: "JavaScript", "Ruby" e "Python"). Talvez ainda queiramos carregar todos os posts de Bob na memória, mas nós queremos mostrar somente aqueles da categoria "JavaScript" por agora. Nessa hora que "encontrar" (find) aparece.
<%= diagram "collections-4", "Selecionando um subgrupo de documentos no cliente.", "pull-center" %>
Da mesma forma que fizemos no servidor, nós vamos usar a função `Posts.find()` para selecionar um subgrupo de nossos dados:
~~~js
// no cliente
Template.posts.helpers({
posts: function(){
return Posts.find({author: 'bob-smith', category: 'JavaScript'});
}
});
~~~
Agora que nós temos uma boa compreensão de como as publicações e subscrições funcionam, vamos mergulhar e rever alguns padrões comuns de implementação.
### Publicação Automática (autopublishing)
Se você criar um projero Meteor do zero (i.e usando `meteor create`), ele vai ter automaticamente o pacote `autopublish` ativado. Para começar, vamos falar sobre o que ele exatamente faz.
O objetivo do `autopublish` é deixar extremamente simples o início da codificação da sua aplicação Meteor, e ele faz isso espelhando automaticamente **todos os dados** do servidor para o cliente, tomando conta das publicações e subscrições para você.
<%= diagram "autopublish", "Autopublish", "pull-center"%>
Como isso funciona? Suponha que você tenha uma coleção chamada `'posts'` no servidor. Então `autopublish` vai automaticamente enviar cada post que encontrar na coleção *posts* no Mongo para uma coleção chamada `'posts'` no cliente (assumindo que exista um).
Se você estiver usando `autopublish`, você não precisa de pensar sobre publicações. Os dados serão ubíquos (presentes em todos os lugares), e as coisas se tornam simples. Claro, existem problema óbvios de ter uma cópia completa do banco de dados da sua aplicação *cacheada* na máquina de cada usuário.
Por esse motivo, *autopublish* é apropriado somente quando você está começando, e ainda não pensou sobre as publicações
### Publicando Coleções Completas
Uma vez que você remova o `autopublish`, você vai rapidamente perceber que todos os seus dados desapareceram do seu cliente. Uma forma simples de tê-los novamente é duplicando o que o *autopublish* faz, publicando uma coleção integralmente. Por exemplo:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find();
});
~~~
<%= diagram "fullcollection", "Publicando uma coleção completa", "pull-center" %>
Nós ainda estamos publicando coleções completas, mas ao menos agora temos controle sobre quais coleções publicamos ou não. Neste caso, estamos publicando a coleção `Posts` mas `Comments` não.
### Publicando Coleções Parcialmente
O próximo nível de controle é publicar somente **parte** de uma coleção. Por exemplo somente os posts que pertencem a um certo autor:
~~~js
Meteor.publish('somePosts', function(){
return Posts.find({'author':'Tom'});
});
~~~
<%= diagram "partialcollection", "Publicando uma coleção parcialmente", "pull-center" %>
<% note do %>
### Nos Bastidores
Se você leu a [documentação do Meteor sobre publicação](http://docs.meteor.com/#publishandsubscribe), você talvez deve estar sobrecarregado com as instruções de se usar `added()` e `ready()` para configurar os atributos de registros no cliente, e se deve ter se esforçado para conciliar isso com os aplicativos Meteor que você viu e nunca usam esses métodos.
A razão é que o Meteor fornece uma importante conveniência: o método `_publishCursor()`. Você nunca viu seu uso? Talvez não diretamente, mas se você retornar um [cursor](/chapter/meteor-vocabulary/) (i.e. `Posts.find({'author':'Tom'})`) em uma função de publicação, isso é exatamente o que o Meteor está usando.
Quando o Meteor vê que a publicação `somePosts` retornou um cursor, ele chama `_publishCursor()` também -- você adivinhou -- publicando este cursor automaticamente.
Aqui o que o `_publishCursor()` faz:
- Checa o nome da coleção no lado do servidor.
- Puxa e encontra documentos a partir do cursor e o envia dentro de uma coleção no lado do cliente *com o mesmo nome*. (Usando `.added()` para fazer isso).
- Sempre que um documento for adicionado, removido ou alterado, ele envia estas mudanças para a coleção no lado do cliente. (Ele usa `.observe()` no cursor e `.added()`, `.changed()` e `.removed()` para fazer isso).
No exemplo acima, nós somos capazes de assegurar que o usuário tenha somente os posts que estiver interessado (os escritos por Tom) disponíveis em cache no lado do cliente.
<% end %>
### Publicando Propriedades Parciais
Nós vimos como publicar somente alguns de nossos posts, mas nós podemos continuar cortando mais coisas! Vamos ver como publicar somente *propriedades* específicas.
Como anteriormente, vamos usar `find()` para retornar um cursor, mas dessa vez vamos excluir alguns campos:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find({}, {fields: {
date: false
}});
});
~~~
<%= diagram "partialproperties", "Publicando propriedades parciais", "pull-center" %>
Claro, nós também podemos combinar ambas as técnicas. Por exemplo, se nós quisermos retornar todos os posts de Tom enquanto deixamos de lado suas datas, podemos escrever assim:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find({'author':'Tom'}, {fields: {
date: false
}});
});
~~~
### Resumindo
Nós vimos como publicar todas as nossas propriedades de todos os documentos e de todas as coleções (com `autopublish`) até como publicar *algumas* propriedades de *alguns* documentos de *algumas* coleções.
Isso cobre o básico do que você pode fazer com as publicações no Meteor, e estas simples técnicas devem ser suficientes para a vasta maioria dos casos.
As vezes, você vai precisar ir além combinando, linkando ou fundindo publicações. Nós vamos cobrir isso em outro capítulo!