Caelum | Ensino e Inovação - Cursos de Java, Scrum, Ruby on Rails


Diminuindo acoplamento de sistemas com REST, e video!

Por Guilherme Silveira em 28/05/10

Muitos ainda me perguntam qual é a real vantagem do REST sobre os modelos mais tradicionais . Ao integrar sistemas, a implementação de acesso ou de processos costuma ser feita de maneira sequencial, onde esperamos resultados específicos de nosso servidor.

Esperar um resultado específico de um servidor é criar um grande acoplamento, e desejamos diminuir isso.

Em clientes REST, e em business processes modelados com REST, atingimos um nível de desacoplamento bem maior. Imagine o sistema a seguir (entre outros) que efetuaria compras em um site como a amazon:


Quando tem um item que quero comprar
E tem um carrinho de compras
Mas nao desejei nada
Entao deseje este produto primeiramente

Quando tem um item que quero comprar
E tem um carrinho de compras
Entao deseje este produto

Quando estou no carrinho
E ainda quero comprar produtos
Entao começe novamente

Quando tem um pagamento
Entao prepare o pagamento

O código acima funciona e pode ser visto em execução na terceira parte da série Rest do zero. Nesse processo modelado como Rest, diversas evoluções poderão ocorrer no servidor sem quebrar o cliente, como por exemplo:

1. Após adicionar um produto ao carrinho, caso o servidor passe a redirecionar para uma sugestão de produto ao invés do carrinho, o cliente continua funcionando normalmente.

2. Caso não encontre um dos produtos, o cliente consegue pagar.

3. Caso o servidor não possua o produto, passará um link para outra empresa que vende ele, e eu executarei o processo na outra empresa, sem perceber nada.

4. Se eu desejar fazer a compra desde o início em outro site, que não a amazon, basta mudar o ponto de entrada de uma URI da amazon para uma URI de outro sistema.

Note que nos beneficiamos com compatibilidade com código antigo, futuro e ainda somos capazes de atingir novos objetivos em outros sistemas sem saber da existência dos mesmos. Além disso passamos a poder comparar preço em diversos sites sem mudar uma linha de código, utilizando media types conhecidos (como Atom ou RDFa).

Esse poder não é alcançado com modelos tradicionais de processo, e por vezes é deixado de lado. É isso que REST e hipermídia traz para nossos sistemas, por exemplo.

Os modelos tradicionais acabam utilizando mensagens que são comandos ao invés de trocar documentos. Apesar de nossa DSL parecer um comando ela é somente uma DSL customizável, executando a troca de documentos que você desejar.

O trabalho de mapear nossos processos de maneira mais genérica através do uso de hipermídia foi descrito em artigos publicados recentemente no portal da ACM.

Imaginemos agora o processo tradicional:

  1. Executa uma busca
  2. Adiciona o produto ao carrinho
  3. Executa outra busca
  4. Adiciona o produto ao carrinho
  5. Paga

Em um sistema de web services clássico, onde temos diversos serviços, como o servidor me diria que devo pagar em outro sistema sem que eu precise escrever nem uma linha de código? Isso iria quebrar o meu cliente, e gerar a necessidade de codificar já pensando nessas falhas e eventuais evoluções do sistema.

Se o servidor passou a suportar somente uma determinada quantidade de produtos no seu carrinho, seu cliente quebrará pois não foi feito adapatdo a isso, o cliente REST se adapta e compra o que for possível.

Se nosso produto ou empresa utiliza esses modelos tradicionais de business process e web services e cada mudança no serviço está implicando em altos custos de mudança nos clientes, temos um grande indício de que esse modelo tradicional apresenta o alto acoplamento que uma arquitetura REST está tentando combater.

Serviços web tradicionais desacoplam um pouco mais que outras maneiras tradicionais de RPC, mas ainda mantém muito acoplamento.

Se o custo de manutenção de seu código cliente está alto, é porque seu web service te acoplou demasiadamente ao servidor e aos schemas relacionados.

A seguir você encontra um vídeo de introdução ao acoplamento que o método tradicional apresenta:

Minimizando acoplamento com REST from Caelum on Vimeo.

  • Share/Bookmark

RESTfulie com C# – O poder do dynamic

Por Luiz Costa em 26/02/10

Há um mês lançamos uma versão inicial do projeto Restfulie para C#, projeto que tem ganhado bastante visibilidade.  Para deixa-lo tão fácil quanto a versão original em Ruby, utilizamos as novas características dinâmicas da versão 4.0 do C#, alterando a estrutura de objetos em tempo de execução. Algumas pessoas acham esse recurso perigoso demaisoutras acham a escolha certa.

Para enxergar o uso da nova palavra chave dynamic, consideremos um recurso REST que representa um pedido com sua representação em XML como a que segue:

<pedido>
  <data>26/12/2009 11:40</data>
  <total>300.00</total>
  <atom:link rel=”refresh” href=”http://www.caelum.com.br/pedidos/1″ xmlns:atom=”http://www.w3.org/2005/Atom”/>
  <atom:link rel=”update” href=”http://www.caelum.com.br/pedidos/1″ xmlns:atom=”…”/>
  <atom:link rel=”pagar” href=”http://www.caelum.com.br/pedidos’/1/pagar” xmlns:atom=”…”/>
  <atom:link rel=”excluir” href=”http://www.caelum.com.br/pedidos/1″ xmlns:atom=”…”/>
  <atom:link rel=”obterCliente” href=”http://www.caelum.com.br/pedidos/1/clientexmlns:atom=” xmlns:atom=”…”/>
</pedido>

É interessante notar que neste XML temos os dados (data, total) e temos ações que podem ser executadas sobre este recurso. Por exemplo, depois que obtivemos a representação deste recurso, é possível executar algumas ações, descritas através de links no próprio xml: pagar, excluir, obterCliente. Para obter a representação de um recurso no restfulie C#  fazemos da seguinte maneira:

dynamic pedido =
  Restfulie.At(“www.caelum.com.br\pedidos\1”).Get();

Repare que o primeiro passo que fazemos ao declarar a variável pedido é ignorar o seu tipo.  Ou melhor, dizer explicitamente que ela é dinâmica, através do tipo dynamic. Isso nos trás uma série de vantagens como, por exemplo, simplesmente acessar suas properties:

Console.WriteLine(string.Format(“A data do pedido é: {0}”, pedido.Data));
Console.WriteLine(string.Format(“O valor total do pedido é: {0}”, pedido.Total));

Além de podermos acessar suas properties, podemos também seguir os links que estão disponíveis na representação, utilizando invocações de métodos:

pedido.Pagar();
pedido.Excluir();

dynamic cliente = pedido.obterCliente();
Console.WriteLine(string.Format(“Nome do cliente: {0}”, cliente.Nome));

Isso é bastante poderoso: perceba que do lado cliente não precisamos definir nada, apenas dizer que uma referência é dinâmica. Mas como esta mágica acontece com o C#?

Além de existir a palavra reservada dynamic existe uma classe DynamicObjectno C#. Diferente de Ruby, em C#, nem todos os objetos podem ser modificados em Runtime. Para que seja possível adicionar comportamento dinâmico em a um objeto, precisamos estender a classe DynamicObject.

Ao estender esta classe, ganhamos a oportunidade de alterar o comportamento do objeto. Quando fizermos então uma invocação a pedido.Total, o que de fato tem que ocorrer aqui? De acordo com o xml de representação do recurso, devemos obter o valor que está na tag <Total>.

Para implementar isso utilizamos um recurso bem parecido com o method_missing em Ruby e, de certa forma, como as dynamic proxies do Java (apesar destas precisarem de interfaces explícitas). Vejamos a classe DynamicXmlResource:

class DynamicXmlResource : DynamicObject {
   private XElement xmlRepresentation;

   public override bool TryGetMember(GetMemberBinder binder,
             out object result) {
     //pseudo código que encontra a tag xml de acordo
     //com o nome da property informada
     object = XmlRepresentation.FindXMLTagWithName( binder.Name)
             .ReadTheValue()
     return result != null;
   }
}

Esta classe estende DynamicObject e tem um atributo que é a representação do recurso em XML (xmlRepresentation). O mais interessante é o método TryGetMember que intercepta qualquer invocação a um Get de uma property e nos permite fazer o que for necessário. No nosso caso, o que é feito é buscar o valor da da property no xml, que nesse caso tem o nome igual ao da tag do xml. Com isso, toda vez que fizermos pedido.Total, o que está acontecendo é a invocação de TryGetMember da classe DynamicXmlResource.

Da mesma forma que existe o método TryGetMember, existem outros que nos permitem alterar o comportamento do objeto. No nosso projeto, também fizemos o uso do TryInvokeMember:

class DynamicXmlResource : DynamicObject {
  private XElement xmlRepresentation;

  public override bool TryGetMember(GetMemberBinder binder, out object result) {
    result = XmlRepresentation.FindXMLTagWithName(binder.Name).ReadTheValue()
    return result != null;
  }

  public override bool TryInvokeMember(InvokeMemberBinder binder,
        object[] args, out object result) {
    // pseudo código para pegar o link no xml a partir do nome do método
    object link = XmlRepresentation.FindRelAttributeWithName(Binder.Name).ReadTheLink();
    if (link == null)
      throw new ArgumentException
        (string.Format("There is not method defined with name:", binder.Name));

    // faz a chamada remota para o servidor através do link.
    HttpRemoteResponse response = 
      (HttpRemoteResponse) this.InvokeRemoteResource(value.ToString(), binder.Name);
    return result != null;
  }
}

Este método também é parecido com o method_missing, mas neste caso ele intercepta a chamada de um método. No caso do restfulie,  quando chamamos um método qualquer, o que ele faz é, procurar na representação XML e descobrir o link relacionado com o nome do método. Depois disso é só fazer uma requisição http. Novamente, quando fizermos a invocação pedido.Pagar(), ela será interceptada pelo TryInvokeMember e executará uma invocação remota para o Restfulie Server.

Utilizando estas características do C# conseguimos alcançar algo muito próximo do que se faz com o Restfulie Ruby, como comparamos a seguir. Em Ruby:

pedido = Restfulie.at(“http://restfulie-test.heroku.com/orders/14”).get
puts pedido.customer-name
pedido.cancel

E, em C#, temos o código tão sucinto quanto:

dynamic pedido = Restfulie.At(“http://restfulie-test.heroku.com/orders/14.xml”).Get();
Console.WriteLine(pedido.customer_name);
pedido.Cancel();

Uma das consequências da utilização de tipos dinâmicos é que perdemos as vantagens da fase da tipagem estática: não há code complete, e pode ser que a gente erre o nome de um membro, como customer_name, e só viremos a saber disso em tempo de execução. Nada que testes unitários bem colocados não resolvam.

Como podemos ver, a utilização de tipos dinâmicos facilita bastante a utilização da api do Restfulie, assim como diversos outros frameworks vem tirando vantagem com o auxílio desse recurso.

Reforçando a tendência, o Java também anda nesse caminho, com a JSR 292, que melhora o suporte de linguagens dinâmicas na JVM. Em abril de 2003 Robert C. Martin escreveu o artigo “As linguagens dinâmicas vão substituir as estáticas?” e, no final deste artigo, ele faz seguinte pergunta: Estaremos todos nós programando numa linguagem dinamicamente tipada em 2010?. Isso não aconteceu, mas com certeza cada vez mais cenários de utilização estão surgindo, e a tendência é cada vez mais forte.

  • Share/Bookmark

Hipermídia e contratos dinâmicos: menor acoplamento

Por Guilherme Silveira em 17/12/09

Nos últimos anos você vem comprando livros em um website: você acessa o site inicial www.amazon.com, procura pelo livro que deseja comprar, adiciona-o ao seu carrinho, escolhe o método de pagamento e finaliza a compra.

Na época do Natal, o site muda: existe agora uma promoção de fim de ano e você se depara com um conteúdo inesperado: existem funcionalidades e informações novas (como um programa de desconto através de cupons). Como reage um humano ao encontrar a mudança com novas possibilidades de iteração em um site?

  • Gritar: “contrato violado! não comprarei mais nada!
  • Ignorar as novas informações e executar o processo
  • Usar o intelecto humano e se aproveitar das novas informações

Como humanos sabemos o quão natural é agir de maneira a ignorar as informações – caso elas não contribuam com meu objetivo – ou tirar proveito delas.

A opção 1 só se concretiza caso existisse um comprometimento total a maneira que o site disponibilizava suas informações e ao processo: se meu acoplamento for alto e o que eu espero seja fixo, imutável. Infelizmente robôs não são ainda capazes de raciocionar como nós e executar a última opção.

O conteúdo hipermídia permite evoluir o servidor com funcionalidades e dados sem quebrar os clientes consumidores por padrão. Ninguém deixaria de comprar pois fornecemos funcionalidades e dados novos em relação aos recursos disponibilizados.

Isso permitiu a evolução de sites por diversos anos sem que usuários enviassem emails para o responsável reclamando da nova função que foi adicionada, dizendo que não utilizarão o sistema pois existe conteúdo extra.

Hipermídia permite um baixo acoplamento entre o cliente e o servidor e pode ser levado para o mundo da automatização: a web dos sistemas. Na web humana, validamos nossos contratos com o usuário final através do uso de testes end-to-end, verificando a existência de funcionalidades como o usuário o faria.

Diversas opções de ferramentas como selenium-rc e webdriver fornecem funcionalidades para garantir que o comportamento esperado não será quebrado com novos releases.

Eles não validam tudo retornado pela requisição, dando espaço para a ::forward-compatibility::, a capacidade de evoluir nosso sistema no servidor sem quebrar o comportamento esperado. Por exemplo, adicionar novas funcionalidades ou campos não relativos ao teste não deve quebrar o mesmo.

Na web para sistemas integrados, a representação mais comum é o xml, que não suporta conteúdo hipermídia, uma vez que uris devem ser tratadas como texto (de acordo com a especificação) então acabamos criando nossos próprios media-types, como vnd/caelum+xml, onde há a definição de como elas devem ser tratadas: o nosso próprio micro formato.

Existem diversas alternativas para criar esquemas forward e backward compatíveis mas infelizmente esse não é o comportamento padrão de arquivos como o formato ::xsd:: e arquitetos não se lembram disso ao definir seus esquemas, o suporte é opcional.

Dentre essas opções, a mais fácil e possivelmente perigosa envolve permitir qualquer tipo de conteúdo em qualquer campo, enquanto outra solução envolve o uso de tipos polimórficos: um perigoso início de schema-hell controlando diversas versões para uma mesma funcionalidade.

Micro formatos como os que podemos criar permitem a definição de uma estrutura fixa e uma dinâmica: um contrato parcialmente fixo, com garantias para validação e compatibilidade, além de parcialmente dinâmico, com liberdade para evolução, diminuindo o acoplamento que seu sistema possuia ao utilizar um esquema totalmente fixo.

Mas a responsabilidade de não quebrar o contrato original fixo ainda é do servidor.

Na web humana, xhtml permite validar a estrutura (o contrato) enquanto é responsabilidade sua (seus testes) não remover o campo de busca de livro, caso contrário o processo não se completa.

Enquanto esquemas permitem a validação de dados, os testes permitem a validação dos processos. Ambos devem ser escritos de maneira a permitir a evolução desacoplada do servidor e do cliente. E quais seriam então as partes dinâmicas do meu contrato?

Os possíveis estados de seu recurso podem variar com o tempo: uma aplicação para empréstimo pode ser só aprovada ou recusada, mas com o passar do tempo a empresa pode decidir a existência de um novo estado: “prolongado”.

As relações entre seu recurso e outros recursos também variam: um cliente pode ter uma lista de serviços contratados atualmente, acessando a sua representação via links. É natural imaginar que surjam novos serviços e que o cliente mude suas contratações.

As transições e operações disponíveis para seus recursos também são dinâmicas: suportando um método HTTP novo ou um novo link não quebra a existência de clientes que consomem as transições e operações existentes até então.

Todo esse dinamismo é guiado através de hiperlinks e conteúdo hipermídia. Como os clientes terão certeza que não quebramos o contrato dinâmico?

Da mesma maneira que implementamos testes para garantir o comportamento esperado, precisamos deles para garantir que o processo não é alterado no servidor.

Os testes end-to-end são a única garantia de que não quebramos os processos junto ao cliente, seja ele humano ou outro serviço.

Esquemas xml podem ser usados de maneira a garantir flexibilidade e compatibilidade, mas não é o comportamento padrão de tal ferramenta: depende muito mais do usuário conhecer e fazer o uso adequado dela.

ATOM é um exemplo que suporta por padrão contratos dinâmicos: ao seguir o Must Ignore, ganhamos forward e backward compatibility. Contratos dinâmicos fornecem dicas para os frameworks, permitindo ao servidor guiar o cliente naquilo que pode executar ou acessar.

A consequência principal de contratos dinâmicos é o baixo acoplamento.

O Restfulie foca no poder do hipermídia como facilitador na evolução a médio e longo prazo: não são URIs elegantes ou a adoção do protocolo HTTP sozinhos que criam sistemas de baixo acoplamento.

  • Share/Bookmark

Arquitetura REST com Java: JAX-RS

Por Sérgio Azevedo Junior em 15/12/09

A necessidade de trocar informações entre aplicações motivou diferentes abordagens para “integração de dados”. Desde soluções simples e questionáveis como utilizar um banco de dados compartilhado, ou realizar troca de arquivos até soluções mais elaboradas que utilizam objetos distribuidos (COM e Corba). Em diversos momentos não temos somente a integração de sistemas diferentes mas a distribuição de um único sistema em diversas partes também pode ser integrada da mesma maneira.

A solução de integração denominada Webservices, que já é relativamente simples de implementar, é a mais utilizada hoje em dia, que vemos em profundidade no curso FJ-31.

A Web é amplamente utilizada e reconhecida principalmente por sua arquitetura robusta, tolerante a falhas e escalável. Quem sustenta a Web nesses fatores e lhe dá todo este poder é o protocolo HTTP. Este protocolo inocente que utilizamos “meio que sem saber” em nossos navegadores de internet está presente na Web inteira, e inclusive em nossos Webservices. Não seria ótimo se eles tirassem proveito das caracteristicas do protocolo HTTP, sem que isso nos desse muito trabalho?

A especifição JSR-311 JAX-RS de Restful webservices (que faz parte agora do Java EE 6) tornou isso simples e possível. Diferentemente do tradicional SOAP – em sua versão amplamente utilizada – e WSDL, o JAX-RS foca um pouco mais em URIs e nos detalhes do protocolo HTTP para se beneficiar de seus recursos.

Como utilizamos o JAX-RS para buscar dados de um Pedido?

@Path("/pedido/{id}")
public class PedidoResource {
 @GET 
@Produces( { MediaType.APPLICATION_XML })
 public Pedido getPedidoById(@PathParam("id") Long id) {
   PedidoDAO pedidoDAO = new PedidoDAO();
   Pedido pedido = pedidoDAO.getPedidoById(id);
   return pedido;
 }
}

Através da classe PedidoResource, disponibilizaremos os dados de nossos pedidos no formato XML. Vamos supor que configuramos que este serviço esteja disponível no endereço: http://caelum.com.br/rest/pedido. Para conseguirmos informações sobre o pedido 10, podemos acessar a url http://caelum.com.br/rest/pedido/10, através de nosso browser de internet favorito. E assim receberiamos um resultado parecido com este:

<pedido>
  <dataPedido>2009-12-10T18:50:57.173-02:00</dataPedido>
  <descricao>Pedido 10</descricao>
  <id>10</id>
  <total>3000.25</total>
</pedido>

A api JAX-RS nos permite trabalhar com o que foi denominado Restful WebServices. E segundo a arquitetura REST nós devemos expor as informações importantes de nossa aplicação como recursos. Para isso precisamos criar uma classe que é definida pela especificação como RootResource. Em nosso exemplo a classe PedidoResource é a nossa , e para ser acessivel aos clientes fornecemos a ela um url através da anotaçao @Path(“/pedido/{id}”), e o próprio JAX-RS reconhece {id} como sendo um parametro que é enviado através do url.

Um recurso pode responder a operações do protocolo HTTP, dentre as quais destacam-se: POST, GET, PUT e DELETE. Nós escolhemos que nosso serviço responderá apenas a solitações do tipo GET, a anotação @GET acima do método getPedidoById é quem define isso. Essas anotações de caminho e de qual método HTTP podem acessar determinado método estão presentes em frameworks como o Spring MVC e o VRaptor.

Depois dizemos que uma solitação GET para nosso recurso irá produzir um resposta do tipo XML, através da anotação @Produces( { MediaType.APPLICATION_XML }).

É impotante destacar que esta anotação não é a responsável por serializar o objeto no formato XML. O JAX-RS usa o JAX-B como serializador padrão, basta para isso colocar a anotação @XmlRootElement na classe desejada, assim como nós fizemos em nossa classe pedido.

@XmlRootElement
public class Pedido {

 private Long id;
 private String descricao;
 private double total;
 private Calendar dataPedido;
 //Getters e Setters...

}

Agora que entendemos melhor o nosso serviço, ou seja, como acessar nosso recurso, podemos olhar melhor para nosso cliente. Como nosso cliente do serviço web pode ser um simples browser de internet, conseguimos usar o browser para consumir nosso serviço web porque eles já estão bem acostumados a realizar operações HTTP do tipo GET.

Mas os browser’s não são nossos únicos clientes. Podemos criar diferentes tipos, e inclusive ter aplicações desktop como clientes. Vejamos um exemplo de uma aplicação cliente, que o usa a API do httpclient do grupo apache:


public class CaelumRestClient {

  public static void main(String[] argsthrows Exception {
    HttpClient httpClient = new HttpClient();

    GetMethod httpMethod = 
      new GetMethod("http://caelum/rest/pedido/20");

    httpMethod.addRequestHeader("Accept""application/xml");
    httpClient.executeMethod(httpMethod);
    Scanner scan = 
      new Scanner(httpMethod.getResponseBodyAsStream());
    PrintStream ps = System.out;
    while (scan.hasNext()) {
      ps.println(scan.nextLine());
    }
    httpMethod.releaseConnection();
  }
}

Este cliente é bem simples, ele faz apenas o mesmo trabalho que o browser já havia feito. Mas nada nos impede de implementar coisas bem mais interessantes. Poderiamos fazer com que este cliente desserializasse nosso objeto pedido. Para isso precisarimos apenas de um parser XML para extrair os valores do xml e depois popular um objeto Pedido com estes dados. A partir daí poderiamos utilizar este objeto em nossa aplicação.

A única limitação é que esta representação XML não nos diz nada sobre quais ações estão disponiveis para o nosso objeto Pedido, nem mesmo quais as relações desse recurso com o mundo afora. Uma alternativa seria utilizar o restfulie framework muito discutido hoje em dia e idealizado por Guilherme Silveira e desenvolvido em conjunto com o pessoal da Caelum, que usa o conceito de hypermedia para expor além dos dados as ações que um determinado objeto pode realizar.

Em breve teremos um post do próprio Guilherme sobre a importância do conteúdo hypermedia na arquitetura REST.

  • Share/Bookmark

SOA sem tentar vender middleware?

Por Fabio Kung em 17/03/09

Na última sexta-feira, estive junto com o Alexandre Magno em um evento organizado pelo pessoal da Stefanini, no Rio de Janeiro. O Alexandre falou um pouco sobre a sua especialidade, Scrum. Eu dei uma palestra sobre SOA e como sempre a expectativa do pessoal era ouvir mais uma palestra cheia de buzzword, que de alguma forma tenta empurrar algum produto de integração e que tenha ESB (Enterprise Service Bus) no nome.

O público era bem misto, com pessoal técnico e não técnico. Bastante gente veio conversar comigo no fim da palestra e demonstraram surpresa com relação a abordagem diferente sobre SOA. Um pouco na linha do fantástico Guerrilla SOA do Jim Webber, tentei falar sobre o assunto sem tentar vender nenhum produto gigante middleware-de-integração. Se você ainda não viu: veja agora, sério. A minha palestra fala um pouco sobre como SOA não precisa ser buzzword, SOA é integração:


Talvez a palestra não faça tanto sentido para quem não esteve presente, mas fiquem a vontade para dar uma olhada e comentar a respeito.

  • Share/Bookmark



Caelum | Ensino e Inovação
São Paulo: Rua Vergueiro, 3185, cj. 87, próximo ao Metrô Vila Mariana   |   Tel. (11) 5571-2751
Rio de Janeiro: Rua Senador Dantas, 80, cj. 307/308 - Centro   |   Tel. (21) 2220-4156 ou 2297-0033
Brasília: SCS Qd. 8 Bl. B-50, Sala 521 - Ed. Venâncio 2000   |   Tel. (61) 3039-4222