Meus camaradas do ISP da Globo estão estudando a migração da arquitetura imperativa com EJBs para uma arquitetura RESTFul.
Esta migração tem muitos aspectos semelhantes à que fizemos no time de cadastro/autenticação no ano passado, e eu já conversei algumas vezes com o pessoal do ISP sobre isso, pois conheço bem o domínio deles. Entretanto, o domínio do ISP é um pouco mais complexo que o do cadastro, e isso torna a modelagem de recursos um pouco mais sofisticada e interessante.
O exemplo que vou descrever aqui é o do Comando CmdFinalizaCompra. Este Comando é invocado no final de qualquer operação do ISP que modifique a cesta de produtos/serviços do assinante. Pelo próprio nome, fica óbvio que ele foi constituído de maneira imperativa (”FazerEstaOperacao”), e o pessoal estava em dúvida sobre como redesenhar este modelo e colocá-lo num desenho declarativo, à maneira RESTFul.
Vou mencionar resumidamente a estrutura de comunicação dos comandos do ISP para que o resto da discussão fique claro. Cada Comando é constituído de 2 classes Java. CmdNomeComando e CmdNomeComandoSrv. A primeira classe corresponde ao Comando do lado do cliente, e a segunda corresponde ao Comando do lado do servidor.
Na execução do comando, o cliente invoca um EJB 2.1 stateless no servidor, e envia a classe CmdNomeComando serializada, via RMI. O servidor recebe a classe, deserializa-a, executa a operação em questão e retorna a classe novamente para o cliente, com ou sem modificações, dependendo do caso.
Este modelo é semelhante à chamada de serviços SOAP, em que é invocada uma operação SOAP com um documento XML de entrada e alguma coisa na saída.
Podemos ver a classe CmdNomeComando como o “parâmetro de entrada” da operação, e a classe CmdNomeComandoSrv como a implementação da operação em questão. Este exemplo do CmdFinalizaCompra envolve vários componentes do modelo do ISP. Não sei dos detalhes de cabeça, mas este processo envolve Assinante, Usuario, Produto, Promocao, Desconto e possivelmente mais alguns elementos do domínio.
Assim, um objeto CmdFinalizaCompra é composto de vários objetos deste domínio. A dúvida deles é sobre a definição dos recursos neste contexto. É uma dúvida pertinente, pois isto foge bastante a um CRUD óbvio. Uma possibilidade de desenho definiria como recursos: Assinante, Usuario, Produto, Promocao e Desconto. Isto seria mapeável diretamente na implementação do domínio deles. O que vocês acham disso?
Bom, não vejo nada errado em expor um recurso Assinante, outro Usuario, etc. Isto pode fazer sentido para outras “operações”, maravilha. Entretanto, para este caso isso levantou a seguinte dúvida para eles: “Eu vou ter que fazer vários POSTs para realizar a operação do CmdFinalizaCompra” ?
Imagine a complexidade de garantir o sucesso transacional dos vários POSTs independentes que compõem esta operação! No universo WS-* existe o conceito chamado Orquestração, no qual um determinado componente agrega e invoca várias operações pequenas em uma única operação/transação. Utilizando nomes pomposos, isto seria um Proxy Service invocando alguns Business Services. Algum de vocês se sente tentado a usar esta abordagem? Eu não!
O que eu sugiro neste exemplo é a criação de um recurso chamado Compra, que pode ser representado por um grafo sofisticado de objetos do domínio do ISP. Para substituir o CmdFinalizaCompra, eu vejo a criação de uma instância do recurso Compra, com todas as informações pertinentes à compra em questão. A criação deste recurso estaria associada a uma única transação, seja lá o que tenha que ser feito por baixo dos panos no servidor.
Na implementação do servidor, eu vejo somente este POST sendo realizado. O cliente não precisa saber (e é melhor que não saiba) como é implementado o domínio no servidor. Ele precisa conhecer os recursos expostos, e como interagir com eles.
Pense numa compra na Amazon, por exemplo. Eles poderiam expôr este mesmo recurso Compra que eu falei, que seria composto pelos produtos, pelo cliente e talvez a opção de pagamento.
Isso seria exposto pelo recurso, mas dentro da Amazon uma compra poderia envolver N operações: reserva de estoque, criação de ordem de entrega, lançamento da cobrança, criação de comissão para o distribuidor, etc. O cliente do Recurso só conhece os detalhes do que foi exposto, e isso não implica em detalhes de implementação no servidor.
Lembrem que um dos principais objetivos para uma arquitetura RESTFul é ter baixo acoplamento, então tenham a liberdade de modelar seu domínio e seus recursos como fizer mais sentido.