Arquitetura RADAR: Aplicações RESTful, Recebedor Trivial

2007 March 30, 06:22 h

Faz tempo que não faço nenhuma tradução por aqui, mas para não perder o costume esbarrei neste artigo de Dave Thomas. É um texto importante por ajudar a entender o conceito de REST e a sugestão de uma nova arquitetura, uma vez que o novo Rails 1.2 tem tudo a ver com isso. Vamos lá.

A Arquitetura RADAR: Aplicações RESTful, Recebedor Trivial

Ontem, postei uma breve nota sobre as mudanças nas convenções de URL no Rails 2.

Sam Aaron respondeu com o comentário:

Talvez eu ainda não tenha entendido essa coisa toda de REST ainda (situação mais provável), mas o re-casamento entre verbo e sujeito (ou pelo menos a perda da habilidade de facilmente distinguir entre eles) me perturba um pouco. Então (só por perguntar) minha questão é: por que um grande Hurra! foi escutado por todo canto?

Vamos ver como chegamos onde estamos. E então olharemos para uma forma diferente de pensar sobre problemas que pode ajudar a simplificar as coisas.

[continue lendo no link abaixo]

Começa com Verbos e Sujeitos

O trabalho de REST de Roy Fielding nos mostrou que o design da interação entre cliente e servidor poderia fazer a grande diferença à confiabilidade e escalabilidade daquela aplicação. Se seguir um conjunto de convenções contidos no REST durante o design de sua aplicação, isso significa que (por exemplo) a rede será capaz de fazer cache de respostas a certas requisições, tirando o trabalho tanto de componentes da rede e da aplicação.

Mas REST era mais do que performance. Oferecia uma maneira de separar as ações executadas por suas aplicações (os verbos) das coisas sobre onde se está agindo (os sujeitos, ou em terminologia REST, os recursos). Além disso, REST disse que os verbos que você usa devem se aplicar consistentemente por todos os seus recursos.

Quando se usa HTTP, os verbos HTTP (como GET, PUT, POST e DELETE) também são verbos que devem ser usados com aplicações REST. Isso faz todo o sentido, porque coisas como roteadores e proxies também entendem esses verbos.

Um dos sucessos construídos sobre esses princípios é o Protocolo de Publicação Atom. Atom permite a clientes puxar, editar e publicar recursos web. Atom é provavelmente mais comumente aplicado para permitir pessoas usar seus editores locais para criar e manter artigos em blogs – software cliente no computador do usuário usa Atom para puxar artigos existentes e salvar de volta artigos editados ou novos.

Vamos olhar como isso acontece com Atom.

Eu abro meu cliente local baseado em Atom. Ele precisa puxar a lista atualizada de todos os artigos que estão no servidor, então ele manda uma requisição GET para uma URL que representa a coleção de artigos: o verbo é GET (pegar), e o recurso é a coleção.

GET /articles

O que volta é uma lista de recursos, incluso em cada um estão uma ou duas URIs. Uma dessas URIs pode ser usada para acessar aquele recurso para edição. Vamos dizer que eu quero editar um artigo em particular. Primeiro, meu software cliente irá puxá-lo, usando a URI dada na coleção:

GET /article/123

Como usuário do software de edição, eu vejo uma versão bem formatada do artigo. Se clicar no botão [EDITAR], aquela visão muda: agora vejo caixas de texto, combo boxes, e assim por diante. Agora posso mudar o conteúdo do artigo. Quando pressionar [SALVAR], a aplicação local então empacota o artigo modificado e envia de volta ao servidor usando uma requisição PUT. O uso de PUT diz “atualize um recurso existente com estas novas coisas”.

Browsers são Idiotas

Se você usa um browser para interagir com um servidor, você está usando um dispositivo half-duplex, baseado em formulários. O host envia um formulário, você preenche e enviar de volta quando está pronto. Isso acaba sendo realmente conveniente para pessoas rodando redes e servidores. Como HTTP não guarda estado e porque aplicações falar com browsers usando um modelo atire-e-esqueça, seus servidores e redes podem aguentar um número enorme de dispositivos. Essa é exatamente a arquitetura que a IBM lançou lá nos anos 70. As aplicações 3270, half-duplex, baseados em votação permitiram mainframes, com menos poder de processamento do que uma torradeira moderna, aguentar dezenas de milhares de usuários simultâneos. O browser não é realmente muito melhor do que um 3270 (com exceção que a resolução é melhor quando vemos pornô). (Recentemente, alguns têm tentado ultrapassar essa simplicidade fazendo aplicações mais interativas baseadas em browsers usando tecnologias como Ajax. Para mim, isso é apenas um grude até jogarmos o browser fora totalmente – Ajax é apenas batom em um porco).

E quão bem browsers jogam em um mundo RESTful?

Bem, para começar eles apenas usam dois verbos HTTP: GET e POST. Isso é um problema? Sim e não.

Sempre podemos nos ajustar sobre a falta de verbos encapsulando o verbo que você queria usar em algum lugar da requisição que mandar ao servidor. Pode ser encapsulado na URL usada para acessar o recurso ou pode ser embutida no corpo da requisição (por exemplo, como um elemento de dado de POST). Ambos funcionam, assumindo que o servidor entenda as convenções. Mas ambas as técnicas também significam que você não está usando REST e o conjunto de verbos HTTP, está simplesmente usando HTTP como um transporte com seu próprio protocolo por cima. E isso significa que a rede é menos hábil a fazer coisas espertas para otimizar o tráfego.

Mas um browser falando HTML sobre HTTP também tem faltas em outras áreas importantes. Ele não está executando nenhuma funcionalidade de aplicação local. Lembra nosso cliente de blog baseado em Atom? Ele é esperto, então sabia como mostrar um artigo para edição. Browsers não tem isso (sem um monte de suporte em termos de Javascript ou Flash). Então, como resultado, pessoas usando idéias RESTful para falar com browsers tem que colocar a esperteza de volta no servidor. Eles inventam novas URLs que (por exemplo) retornam recursos, mas retornam todo embrulhado no HTML necessário para mostrá-lo como um formulário para edição-baseada-em-browser. É assim que Rails funciona. Usando as convenções de Rails, se quiser puxar um artigo para edição no browser, eu posso usar:

GET /article/1

Se, em vez disso, quiser permitir o usuário editar o recurso, eu mando:

GET /article/1;edit

A aplicação então envia um formulário pré-preenchido com os dados do artigo. Quando eu submeto, o conteúdo do formulário é enviado de volta ao servidor, junto com campos escondidos que dizem ao servidor para fazer de conta que o POST de HTTP requisitado na realidade é uma requisição PUT RESTful – o resultado é que o recurso é atualizado no servidor.

A notação ;xxx parece que apenas jogada lá. Acho que nesse caso, aparências não enganam – esses modificadores realmente foram uma pensamento posterior, adicionados quando se tornou aparente que um protocolo puramente parecido com Atom realmente nao faria todo o necessário em uma aplicação web baseada em browsers do mundo real. Isso porque assume-se que um cliente de um servidor Atom faça mais do que apenas ser um visualizador. Um cliente Atom não diria “puxe esse recurso e (oh, e por falar nisso) me envie como um formulário para que eu possa editá-lo”. O cliente deveria apenas dizer GET, fazer o que tem que fazer e então mandar com PUT.

Então, em puro REST, o cliente é um ponto (ou talvez até no comando) de uma troca. Mas quando se fala com um browser idiota, o cliente não é melhor do que um intermediário entre o humano e a aplicação. É por isso que foi necessário adicionar coisas como ;edit.

Aplicações Independentes de Cliente

Um dos benefícios de desenvolver aplicações contra verbos limitados oferecidos como HTTP REST é que você pode (em teoria) suportar muitos clientes diferentes com o mesmo código. Você poderia escrever um servidor de blogging que (por exemplo) olharia para o cabeçalho Accept de HTTP para decidir o que mandar de volta ao cliente. A resposta a uma requisição GET /articles seria uma página HTML bem formatada se o cliente quisesse HTML, um envelope XML se o cliente pedisse por XML, ou uma coleção Atom se o cliente pedisse por application/atomcoll+xml. Em terminologia Rails, isso é gerenciado pelo respond_to que é duplicado em cada ação de controller de um servidor RESTful.

Mas os benefícios vão mais fundo do que simplemente ser capaz de servir diferentes estilos de resposta.

O conjunto de verbos HTTP mapeia (com um pouco de mexida em URL) direitinho com o conjunto de verbos de bancos de dados. POST, GET, PUT e DELETE mapeiam para verbos CRUD: Criar,®Ler, (U)Atualizar, Deletar.

Então, em teoria pelo menos, você deveria ser capaz de escrever sua aplicação com um fino verniz sobre um conjunto de recursos. A aplicação exporta os verbos para manipular os recursos e os clientes se divertem acessando-os. Isso é similar com a visão de Naked Objects.

Entretanto, eu pessoalmente acho que foi otimista tentar tratar dois estilos de interação (clientes espertos e idiotas) usando o mesmo protocolo. A feiúra de ;xxx era uma dica.

Acho que existe muito mérito em seguir o modelo baseado em CRUD para interagir com os recursos de sua aplicação. Não estou convencido de que todo o trabalho de dobrar interações de browsers idiotas com um conjunto de verbos baseados em REST, e então extendê-los para fazê-los funcionar, seja reconpensador. Para colocar de outro jeito, acredito que a disciplina de convenções impostas pelo pensamento na interface da sua aplicação como REST vale a pena. Isso leva a designs mais limpos e fáceis de entender. Os obstáculos que tem que se pular para implementar isso com um browser como cliente parece sugerir que uma aderência escravista ao protocolo pode ser uma puxada longe demais.

Um Modelo Alternativo

Isso significa que estou fora de aproximações baseadas em RESTful, CRUD para desenvolvimento de aplicações? Nada disso. Para algumas categorias de aplicações, acho que é uma grande maneira de estruturar seu código. Mas REST não foi desenhado para falar com pessoas. Então vamos aceitar esse fato quando criarmos aplicações. E, enquanto estamos nisso, vamos tirar vantagem do fato que HTTP é um transporte flexível. E vez de tentar desenhar aplicações monolíticas que tem ambas funcionalidades CRUD e a esperteza para falar HTML com usuários finais. Por que não separar em duas aplicações menores e mais simples?

Coloque a lógica da aplicação principal em um servidor RESTful. É aí onde todo o acesso a recursos no estilo CRUD fica.

Então, escreva um segundo servidor-proxy. Esse é um filtro HTTP, sentado entre browsers idiotas e seus recursos-núcleo. Quando usuários baseados em browser precisarem interagir com seus recursos, eles na verdade se conectam ao proxy. Então este fala REST com seu servidor principal e interpreta respostas RESTful em uma forma útil ao browser. E esse filtro não precisa ser uma camada de apresentação idiota – não há nada que impeça funcionalidades adicionais também. Coisas como menus, preferências de usuários e outras coisas poderiam ficar aí.

Onde esse filtro se encaixa? Isso depende. Algumas vezes faz sentido colocá-lo rodando localmente no computador local do próprio usuário. Nesse caso estamos de volta na situação como quando olhamos o software de blogging. Algumas vezes faz sentido ter o proxy rodando na rede. Nesse caso você ganha algumas flexibilidades arquiteturais interessantes – não há regra que diz que você precise colocar fora seus servidores de REST e apresentação. Potencialmente, isso pode levar a melhores tempos de resposta, menos carga na rede e segurança maior.

Com essa aproximação, podemos deixar de lado as gambiarras em nossas URLs para fazer dados POST agir de maneira RESTful. E podemos deixar de lado os feios respond_to em nosso código Rails. Poderíamos até mesmo ser capazes de melhorar reusabilidade, compartilhando ambos recursos de servidor e servidores de apresentação entre aplicações.

Vamos chamar isso de RADAR. Temos um ‘RESTful Application talking to Dumb-Ass Recipients’.

Isso funciona? Não sei, você me diga.

tags: obsolete restful

Comments

comentários deste blog disponibilizados por Disqus