Esse artigo faz parte da revista Java Magazine edição 56. Clique aqui para ler todos os artigos desta edição

yle="FONT-SIZE: 12pt; FONT-FAMILY: Verdana">Web services REST

Uma abordagem prática

Implementação de web services flexíveis e poderosos explorando os recursos do HTTP

Na Edição 54 fizemos uma análise pragmática dos web services REST e WS-*, visando comparar de forma imparcial as características mais importantes das duas linhas de desenvolvimento. Na edição passada, passamos para uma abordagem prática, partindo de um exemplo de serviços do Mercado Livre dentro do contexto de leilões de produtos.

A implementação destes serviços no artigo anterior foi feita com uso da linha de web services WS-*, através da pilha do Apache Axis 2. Complementando esta série sobre desenvolvimento de web services, neste artigo iremos partir do mesmo problema de leilões do Mercado Livre, mas realizar a implementação de uma maneira RESTful.

Os web services REST surgiram de uma proposição na tese de doutorado de Roy Fieding, que entre outras coisas é o principal autor da especificação HTTP e um dos fundadores da Apache Software Foundation.

A sigla REST significa REpresentational State Transfer, sendo um estilo de arquitetura que tem como princípio central a manipulação de Recursos. A principal característica dos serviços que usam verdadeiramente este estilo é a exploração rica do protocolo HTTP.

Provavelmente a maior razão por trás do sucesso atual dos serviços REST comparando com a pilha WS-* é a simplicidade do seu conjunto de regras básicas. O consórcio de empresas que participou da definição dos padrões WS-* elaborou especificações para cenários bastante diversificados. Tentou-se elaborar padrões para todas as situações que pudessem surgir para desenvolvimento de serviços. Como conseqüência disso, chegamos num ponto onde apenas indivíduos que participam ativamente deste consórcio conseguem ter domínio amplo sobre os padrões WS-*. Desenvolvedores que utilizam esta pilha de padrões para resolver problemas reais conhecem apenas um sub-conjunto bem menor das especificações geradas pelo consórcio.

Na direção contrária deste movimento, os desenvolvedores de serviços REST se fundamentaram em um conjunto de idéias simples e fortemente associado ao que é comumente usado na internet. As principais tecnologias adotadas envolvem HTTP e XML. Os arquitetos desta linha de implementação foram progressivamente adotando soluções inteligentes para resolver problemas comuns. Com os blueprints já disponíveis e o amadurecimento das discussões, estamos avançando em um caminho de soluções poderosas que não perdem a simplicidade essencial do REST.

O objetivo deste artigo é explorar novamente o problema de leilões do Mercado Livre, e detalhar todos os aspectos envolvidos em uma implementação de serviços REST. Comparando duas abordagens diferentes para o mesmo problema, pode-se ter uma boa idéia dos conceitos principais de cada abordagem. Estudar a arquitetura utilizada para este artigo e confrontá-la com a do artigo passado permitirá que o leitor perceba nuances importantes das duas formas de implementação de web services, e então ter na mente os principais questionamentos que se deve fazer ao escolher uma das duas para resolver um determinado problema.

Para este artigo, decidimos não introduzir tecnologias e frameworks que pudessem ocultar detalhes importantes dos serviços REST. A idéia é adotar uma implementação que permita apresentar de forma nítida os conceitos envolvidos. Em artigos posteriores, será feito o detalhamento de frameworks e componentes que podem refinar a arquitetura e agregar produtividade ao desenvolvimento.

Apresentação do problema

O problema que buscaremos resolver é o mesmo da edição passada, e envolve serviços relacionados a um leilão do Mercado Livre. Um usuário cadastrado no site coloca para venda um produto (novo ou usado), definindo um valor para o lance inicial e então aguarda pelas ofertas de compra por parte de outros usuários interessados no produto.

Após receber algumas ofertas pelo produto, em determinado momento o vendedor decide aceitar a melhor oferta recebida, e então vende o produto para o comprador que fez esta oferta, encerrando o leilão. Em seguida ocorrem os trâmites de pagamento e entrega do produto (que não trataremos aqui) e no final, vendedor e comprador avaliam um ao outro, o que é essencial para os usuários sentirem maior segurança ao realizar negociações futuras.

Na modelagem desta aplicação, quatro classes serão utilizadas: Usuário, Item, Oferta e Avaliação. Os serviços a serem oferecidos podem ser vistos no quadro “Serviços oferecidos no processo de leilão”. Para permitir uma comparação adequada, os serviços em questão são os mesmos do artigo anterior.

 

Serviços oferecidos no processo de leilão

A primeira etapa na elaboração de uma arquitetura orientada a serviços é a identificação dos serviços que serão disponibilizados para interação com a aplicação e/ou empresa em questão.

No nosso exemplo do leilão, os serviços envolvidos manipulam Usuários, Itens, Ofertas e Avaliações, conforme identificado na modelagem. A Tabela 1 descreve as operações que poderão ser realizadas por quem estiver se integrando ao processo de leilão.

 

Serviço

Descrição

Anunciar item

Permite que um usuário coloque um produto à venda.

Buscar itens do vendedor

Pesquisa os itens à venda de um vendedor.

Cadastrar usuário

Realiza o cadastro de um novo usuário no site.

Realizar oferta

Permite que um comprador faça uma oferta por um produto.

Retirar oferta

Permite a remoção de uma oferta por parte do comprador.

Buscar ofertas do item

Pesquisa por todas as ofertas feitas sobre um produto.

Buscar melhor oferta

Busca a melhor oferta feita até o momento sobre um produto.

Aceitar melhor oferta

Permite que um vendedor aceite a melhor oferta feita sobre o seu produto, e com isso encerre o leilão do mesmo.

Avaliar usuário

Realiza a avaliação de um usuário por parte de outro usuário, após o término do processo de compra.

Buscar avaliações do usuário

Pesquisa por todas as avaliações recebidas por um usuário.

Tabela 1. Serviços oferecidos para interação com o processo de leilão

Visão geral da implementação

Já vimos na edição passada uma abordagem com o estilo de serviços WS-*. Na aplicação de exemplo desenvolvida foram codificadas todas as camadas. Desde a persistência com DAOs, passando por uma camada de ServiceLayer (padrão de projeto proposto por Martin Fowler, e descrito no mesmo artigo) e chegando aos web services implementados com uso do Apache Axis 2.

Para a versão RESTful dos serviços do Mercado Livre, reaproveitaremos toda a estrutura definida no artigo anterior, com exceção dos web services, que deixam de utilizar a pilha WS-* para definir um protocolo de comunicação com extensa exploração do HTTP.

Com este re-uso da arquitetura e componentes da implementação anterior, omitimos os detalhes de configuração de ferramentas (Derby, Tomcat e Eclipse), pois isto pode ser conferido adequadamente no outro artigo.

Para os web services REST, foi escolhida uma implementação simplificada, que fará uso apenas de um Servlet como Front Controller e algumas classes de processamento das operações. O principal ponto no qual o leitor deve prestar atenção é na definição do protocolo de comunicação a ser utilizado. À medida que formos avançando, destacaremos os aspectos mais relevantes do protocolo definido.

Modelagem com recursos

O ponto de partida do desenvolvimento RESTful é definir quais são os recursos envolvidos, com base nos requisitos do sistema e nos serviços que se deseja oferecer. No nosso exemplo, esta etapa não é complexa. Os recursos que manipularemos são: Usuario, Item, Oferta e Avaliacao.

Embora esta identificação tenha sido trivial no domínio que definimos, em alguns casos este processo pode ser um dos mais complexos na modelagem da aplicação. De uma maneira geral, quanto mais a aplicação se aproxima de um CRUD, mais fácil é a identificação dos recursos.

Tendo definido os recursos e os serviços que precisamos oferecer, é necessário definir as manipulações possíveis sobre os recursos existentes. Esta etapa é a tradução de operações de negócio em interações diretas sobre usuários, ítens, ofertas e avaliações. Esta tradução feita explorando os recursos do HTTP nos levará a um conjunto de URIs que a aplicação oferece para os clientes.

Abordagem declarativa X imperativa

Complementando as URIs, a lista de métodos HTTP aceitos em cada uma delas indicará quais são as operações suportadas pela aplicação. Este ponto é primordial em um protocolo REST. A URI deve indicar o que você está manipulando e o método (ou verbo) HTTP indicará como você está manipulando.

Esta característica diferencia conceitualmente as abordagens REST e WS-*. Este casamento do verbo HTTP com a URI é classificada como uma maneira declarativa de se desenvolver web services. A primeira linha do cabeçalho HTTP já declara o que está sendo feito pela requisição RESTful em questão.

Nos web services WS-*, a informação da operação que está sendo realizada fica encapsulada no corpo da requisição. Mesmo quando a camada de transporte das mensagens SOAP é HTTP, a URI não esclarece de forma alguma a operação envolvida. A informação dos serviços disponíveis fica descrita por elementos operation de um documento WSDL, geralmente em um formato fazerEssaOperacao. Esta maneira de desenvolver web services é classificada como imperativa.

Voltando à definição das URIs REST, uma recomendação importante é que a URI deve conter todas as informações necessárias para identificar o recurso envolvido. Parâmetros via query string idealmente não devem ser utilizados. A especificação do protocolo HTTP (mais precisamente na seção 13.9 da RFC 2616) determina que respostas a requisições GET que contenham query strings não devem ser obtidas do cache do servidor web. Isto significa que o servidor pode cachear a resposta a uma URI como /usuario/1234, mas ele não irá cachear a resposta a uma requisição a /usuario?id=1234.

O uso da abordagem declarativa e com URIs significativas aumenta também a clareza e traz mais alguns benefícios interessantes. Entre eles está a possibilidade de encaminhar URIs adiante. Você pode cadastrar um determinado usuário, obter a URI de acesso ao mesmo na resposta ao cadastro e encaminhá-la adiante com facilidade. Todavia, você não seria capaz de invocar uma operação SOAP de cadastro de usuário e encaminhar uma operação de busca deste usuário cadastrado.

Outra vantagem é a capacidade de identificar já na camada HTTP o particionamento do acesso a diferentes partes da aplicação. Você será capaz de mapear facilmente a distribuição dos acessos às URIs definidas para a aplicação. Esta informação será possivelmente útil para alocar mais máquinas para o tratamento das URIs com maior tráfego. Sem URIs significativas seria necessário extrair as métricas de camadas inferiores da aplicação, o que é mais complicado.

Dissertarmos sobre a relevância das URIs, métodos HTTP e o formato declarativo das chamadas REST. Podemos agora detalhar as escolhas de URIs e métodos que foram feitas para nossa implementação. O quadro “URIs e métodos HTTP do processo de leilão” contém as URIs tratadas pela aplicação, a definição dos métodos aceitos por cada uma delas e o formato de dados trocados pelos serviços e seus clientes.

 

URIs e métodos HTTP do processo de leilão

...

Quer ler esse conteúdo completo? Tenha acesso completo