>

>

>

>

AirbnbEng

Follow

>

>

Nov 11, 2013 – 10 min leia-se

>
>

>

>

Por Spike Brehm

Este post foi postado transversalmente no VentureBeat.

Na Airbnb, temos aprendido muito nos últimos anos enquanto construímos experiências ricas na web. Nós mergulhamos no mundo do aplicativo de página única em 2011 com nosso site móvel, e desde então lançamos Wish Lists e nossa página de busca recém-redesenhada, entre outras. Cada um deles é um grande aplicativo JavaScript, o que significa que a maior parte do código roda no navegador para suportar uma experiência mais moderna e interativa.

Esta abordagem é comum hoje em dia, e bibliotecas como Backbone.js, Ember.js e Angular.js tornaram mais fácil para os desenvolvedores construir estes ricos aplicativos JavaScript. Nós descobrimos, no entanto, que estes tipos de aplicativos têm algumas limitações críticas. Para explicar porquê, vamos primeiro fazer um rápido desvio pela história dos aplicativos web.

JavaScript Grows Up

Desde o início da Web, a experiência de navegação tem funcionado assim: um navegador web requisitaria uma página em particular (digamos, “http://www.geocities.com/”), fazendo com que um servidor em algum lugar na Internet gerasse uma página HTML e a enviasse de volta através do fio. Isto funcionou bem porque os browsers não eram muito poderosos e as páginas HTML representavam documentos que eram na sua maioria estáticos e auto-contidos. O JavaScript, criado para permitir que as páginas web fossem mais dinâmicas, não permitia muito mais do que apresentações de slides de imagens e widgets de escolha de datas.

Após anos de avanços na computação pessoal, os tecnólogos criativos levaram a web aos seus limites, e os navegadores web evoluíram para se manterem atualizados. Agora, a Web amadureceu para uma plataforma de aplicativos completa, e os rápidos tempos de execução JavaScript e padrões HTML5 permitiram aos desenvolvedores criar os aplicativos ricos que antes só eram possíveis em plataformas nativas.

O aplicativo de página única

Não demorou muito para que os desenvolvedores começassem a construir aplicativos inteiros no navegador usando JavaScript, tirando proveito dessas novas capacidades. Apps como o Gmail, o exemplo clássico do aplicativo de página única, poderiam responder imediatamente às interações do usuário, não precisando mais fazer uma ida e volta ao servidor apenas para renderizar uma nova página.

Bibliotecas como Backbone.js, Ember.js, e Angular.js são frequentemente referidas como bibliotecas MVC (Model-View-Controller) ou MVVM (Model-View-ViewModel) do lado do cliente. A arquitetura MVC típica do lado do cliente se parece com isto:

A maior parte da lógica da aplicação (vistas, templates, controladores, modelos, internacionalização, etc.) vive no cliente, e fala com uma API para dados. O servidor pode ser escrito em qualquer linguagem, como Ruby, Python, ou Java, e na maior parte das vezes, ele lida servindo uma página inicial de barebones de HTML. Uma vez que os arquivos JavaScript são baixados pelo navegador, eles são avaliados e o aplicativo do lado do cliente é inicializado, pegando dados da API e renderizando o resto da página HTML.

Isso é ótimo para o usuário porque uma vez que o aplicativo é carregado inicialmente, ele pode suportar navegação rápida entre páginas sem atualizar a página, e se feito corretamente, pode até funcionar offline.

Isso é ótimo para o desenvolvedor porque o aplicativo idealizado de página única tem uma clara separação de preocupações entre o cliente e o servidor, promovendo um bom fluxo de desenvolvimento e evitando a necessidade de compartilhar muita lógica entre os dois, que muitas vezes são escritos em linguagens diferentes.

Na prática, entretanto, existem algumas falhas fatais com esta abordagem que impedem que ela seja correta para muitos casos de uso.

SEO

Uma aplicação que só pode rodar no lado do cliente não pode servir HTML para rastreadores, então ela terá SEO pobre por padrão. Web crawlers funcionam fazendo um pedido a um servidor web e interpretando o resultado; mas se o servidor retorna uma página em branco, não tem muito valor. Existem soluções, mas não sem saltar por alguns hoops.

Performance

Pelo mesmo motivo, se o servidor não renderiza uma página inteira de HTML mas espera que o JavaScript do lado do cliente o faça, os utilizadores irão experimentar alguns segundos críticos de página em branco ou carregar o spinner antes de ver o conteúdo na página. Há muitos estudos mostrando o efeito drástico que um site lento tem sobre os usuários e, portanto, sobre as receitas. A Amazon afirma que cada redução de 100ms no tempo de carregamento de páginas aumenta a receita em 1%. O Twitter passou um ano e 40 engenheiros reconstruíram seu site para renderizar no servidor ao invés do cliente, alegando uma melhoria de 5x no tempo de carregamento percebido.

Maintaintainability

Embora o caso ideal possa levar a uma bela e limpa separação de preocupações, inevitavelmente alguns bits de lógica de aplicação ou de visualização acabam sendo duplicados entre cliente e servidor, muitas vezes em idiomas diferentes. Exemplos comuns são a formatação de data e moeda, validações de formulário e lógica de roteamento. Isto faz da manutenção um pesadelo, especialmente para aplicações mais complexas.

Alguns desenvolvedores, incluindo eu próprio, sentem-se mordidos por esta abordagem – muitas vezes é apenas depois de terem investido tempo e esforço para construir uma aplicação de página única que se torna claro quais são os inconvenientes.

A Hybrid Approach

No final do dia, nós realmente queremos um híbrido das novas e antigas abordagens: queremos servir HTML totalmente formatado a partir do servidor para performance e SEO, mas queremos a velocidade e flexibilidade da lógica da aplicação do lado do cliente.

Para este fim, nós temos experimentado na Airbnb com aplicações “Isomorphic JavaScript”, que são aplicações JavaScript que podem rodar tanto no lado do cliente quanto no lado do servidor.

Uma aplicação isomórfica pode ter este aspecto, apelidada aqui de “Client-server MVC”:

>

>

Neste mundo, alguma da sua aplicação e lógica de visualização pode ser executada tanto no lado do servidor como no lado do cliente. Isto abre todos os tipos de portas – optimizações de performance, melhor manutenção, SEO por defeito, e aplicações web mais stateful.

Com o Node.js, um servidor rápido e estável em JavaScript runtime, podemos agora tornar este sonho numa realidade. Criando as abstrações apropriadas, podemos escrever nossa lógica de aplicação de forma que ela rode tanto no servidor quanto no cliente – a definição de JavaScript isomórfico.

JavaScript isomórfico no Wild

Esta idéia não é nova – Nodejitsu escreveu uma ótima descrição da arquitetura JavaScript isomórfica em 2011 – mas tem sido lenta de ser adotada. Já surgiram alguns frameworks isomórficos.

Mojito foi o primeiro framework isomórfico de código aberto a obter alguma imprensa. É um framework avançado, baseado no Node.js, mas sua dependência de YUI e Yahoo! não tem levado a muita popularidade na comunidade JavaScript desde que eles o abriram em abril de 2012.

Meteor é provavelmente o projeto isomórfico mais conhecido atualmente. O meteoro é construído desde o início para suportar aplicativos em tempo real, e a equipe está construindo um ecossistema inteiro em torno de seu gerenciador de pacotes e ferramentas de implantação. Como o Mojito, é um framework grande e opinante do Node.js, porém tem feito um trabalho muito melhor envolvendo a comunidade JavaScript, e sua tão esperada versão 1.0 está bem próxima. Meteoros é um projeto para ficar de olho – tem uma equipe all-star, e arrecadou $11,2 milhões de dólares de Andreessen Horowitz – inédito para uma empresa totalmente focada no lançamento de um produto open-source.

Asana, o aplicativo de gerenciamento de tarefas fundado pelo cofundador do Facebook Dustin Moskovitz, tem uma história isomórfica interessante. Não prejudicando para o financiamento, considerando o status de Moskovitz como o bilionário mais jovem do mundo, Asana passou anos em R&D desenvolvendo sua estrutura de código fechado Luna, um dos exemplos mais avançados de JavaScript isomórfico ao redor. Luna, originalmente construído sobre v8cgi nos dias antes da existência do Node.js, permite que uma cópia completa do aplicativo seja executada no servidor para cada sessão do usuário. Ele executa um processo de servidor separado para cada usuário, executando o mesmo código da aplicação JavaScript no servidor que está rodando no cliente, permitindo toda uma classe de otimizações avançadas, tais como suporte robusto offline e atualizações instantâneas em tempo real.

Lançamos uma biblioteca isomórfica própria no início deste ano. Chamada Rendr, ela permite que você construa uma aplicação Backbone.js + Handlebars.js de página única que também pode ser totalmente renderizada no lado do servidor. Rendr é um produto de nossa experiência reconstruindo o aplicativo web móvel Airbnb para melhorar drasticamente os tempos de carregamento de páginas, o que é especialmente importante para usuários em conexões móveis de alta latência. Rendr se esforça para ser uma biblioteca e não um framework, por isso resolve menos problemas para você em comparação com Mojito ou Meteor, mas é fácil de modificar e estender.

Abstraction, Abstraction, Abstraction

Que esses projetos tendem a ser grandes, frameworks web full-stack fala sobre a dificuldade do problema. O cliente e o servidor são ambientes muito diferentes, por isso devemos criar um conjunto de abstrações que desacoplam nossa lógica de aplicação das implementações subjacentes, para que possamos expor uma única API ao desenvolvedor da aplicação.

Roteamento

Queremos um único conjunto de rotas que mapeie os padrões URI para os manipuladores de rotas. Nossos manipuladores de rotas precisam ser capazes de acessar cabeçalhos HTTP, cookies e informações URI, e especificar redirecionamentos sem acessar diretamente window.location (navegador) ou req e res (Node.js).

Recuperação e dados persistentes

Descrevemos os recursos necessários para renderizar uma determinada página ou componente independentemente do mecanismo de fetching. O descritor de recursos pode ser um simples URI apontando para um endpoint JSON, ou para aplicações maiores, pode ser útil encapsular recursos em modelos e coleções e especificar uma classe de modelo e uma chave primária, que em algum momento seria traduzida para um URI.

View rendering

Se escolhermos manipular diretamente o DOM, aderir a modelos HTML baseados em strings, ou optar por uma biblioteca de componentes UI com uma abstração de DOM, precisamos ser capazes de gerar marcas isomorfas. Devemos ser capazes de renderizar qualquer view tanto no servidor quanto no cliente, dependendo das necessidades da nossa aplicação.

Building and packaging

Realmente escrever código isomórfico da aplicação é apenas metade da batalha. Ferramentas como Grunt e Browserify são partes essenciais do fluxo de trabalho para realmente colocar a aplicação em funcionamento. Pode haver uma série de passos de compilação: compilação de templates, incluindo dependências do lado do cliente, aplicação de transformadas, minificação, etc. O caso simples é combinar todo o código da aplicação, vistas e templates em um único pacote, mas para aplicações maiores, isso pode resultar em centenas de kilobytes para download. Uma abordagem mais avançada é criar pacotes dinâmicos e introduzir a carga de ativos preguiçosos, porém isso se torna rapidamente complicado. Ferramentas de análise estática como o Esprima podem permitir que desenvolvedores ambiciosos tentem otimização avançada e metaprogramação para reduzir o código da placa de caldeira.

Compondo Juntos Módulos Pequenos

Ser o primeiro a comercializar com um framework isomórfico significa que você tem que resolver todos esses problemas de uma só vez. Mas isso leva a frameworks grandes e pesados, difíceis de adotar e de integrar em um aplicativo já existente. À medida que mais desenvolvedores lidam com este problema, veremos uma explosão de pequenos módulos reutilizáveis que podem ser integrados para construir aplicações isomórficas.

Acontece que a maioria dos módulos JavaScript já podem ser usados isomorficamente com pouca ou nenhuma modificação. Por exemplo, bibliotecas populares como Underscore, Backbone.js, Handlebars.js, Moment, e até jQuery podem ser usadas no servidor.

Para demonstrar este ponto, eu criei um aplicativo de exemplo chamado isomorphic-tutorial que você pode conferir no GitHub. Combinando alguns módulos, cada um que pode ser usado isomorficamente, é fácil criar uma simples aplicação isomórfica em apenas algumas centenas de linhas de código. Ele usa Director para roteamento baseado em servidor e navegador, Superagent para solicitações HTTP e Handlebars.js para templates, tudo construído sobre um aplicativo Express.js básico. É claro, à medida que um aplicativo cresce em complexidade, é preciso introduzir mais camadas de abstração, mas minha esperança é que à medida que mais desenvolvedores experimentarem isso, surgirão novas bibliotecas e padrões.

The View From Here

As mais organizações se sentem confortáveis rodando o Node.js em produção, é inevitável que mais e mais aplicativos web comecem a compartilhar código entre seu cliente e o código do servidor. É importante lembrar que o JavaScript isomórfico é um espectro – ele pode começar com apenas compartilhar templates, progredir para ser uma camada inteira de visualização da aplicação, até a maior parte da lógica de negócios da aplicação. Exatamente o que e como o código JavaScript é compartilhado entre ambientes depende inteiramente da aplicação sendo construída e seu conjunto único de restrições.

Nicholas C. Zakas tem uma bela descrição de como ele prevê que as aplicações começarão a puxar sua camada de UI para o servidor a partir do cliente, permitindo otimizações de performance e manutenção. Um aplicativo não precisa arrancar seu backend e substituí-lo pelo Node.js para usar JavaScript isomórfico, essencialmente jogando fora o bebê com a água do banho. Ao invés disso, criando APIs sensíveis e recursos RESTful, o backend tradicional pode viver junto com a camada Node.js.

Na Airbnb, nós já começamos a reequipar nosso processo de construção do lado do cliente para usar ferramentas baseadas no Node.js como Grunt e Browserify. Nossa principal aplicação Rails pode nunca ser totalmente substituída por uma aplicação Node.js, mas abraçando essas ferramentas fica cada vez mais fácil compartilhar certos pedaços de JavaScript e templates entre ambientes.

Você ouviu aqui primeiro – dentro de alguns anos, será raro ver uma aplicação web avançada que não esteja rodando algum JavaScript no servidor.

Aprenda Mais

Se essa idéia te excita, venha conferir o workshop de JavaScript Isomórfico que estarei ensinando no DevBeat na terça-feira, 12 de novembro em São Francisco, ou na Assembléia Geral na quinta-feira, 21 de novembro. Nós vamos hackear juntos o aplicativo isomorphic-tutorial Node.js que eu criei para demonstrar como é realmente fácil começar a escrever aplicativos isomórficos.

AirbnbEng.

Deixe uma resposta

O seu endereço de email não será publicado.