terça-feira, 15 de maio de 2018

Desacople sua API REST!

#engenhariaDeSoftware #REST #API

Tenho visto muitos problemas com APIs REST, mas há um que é extremamente grave e as pessoas não se dão conta: Acoplamento entre Cliente e Servidor da API.

Neste breve artigo, procurarei mostrar quais são os problemas e as alternativas a esta prática.




Java é mais um problema do que uma solução

Depois de anos trabalhando com Java, essa é a conclusão a que cheguei. E não é por causa da linguagem Java ou da JVM, mas por causa da filosofia da Comunidade de desenvolvedores e apoiadores.

A principal parte da filosofia que causa problemas é: "Tudo tem que ser feito em Java ou em alguma tecnologia relacionada".

E assim é com JAX-RS e JPA também. Os desenvolvedores ficam tão "viciados" em tecnologias Java, que se esquecem que o resto do mundo pode pensar diferente! E isso, com APIs REST, é mortal.

Entidades JPA

Pessoalmente, considero ORM (Object-Relational Mapping) um desastre! Por que? Porque limita nossas escolhas a um único modelo de persistência, forçando a barra para usar OOP em problemas para os quais ela não foi pensada.

Bom, mas independentemente disso, 10 entre 10 desenvolvedores Java ainda usam ORM, através da JPA (Java Persistence API). Até ai, o problema é só deles.

Porém, quando resolvem criar REST APIs, querem utilizar as mesmas entidades JPA como "payload" das mensagens. No POST, no GET e no PUT, trafegam uma serialização das mesmas classes de entidades.

Recursos (REST)

A arquitetura REST tem 6 restrições, que orientam o desenvolvimento de APIs. Uma delas é muito importante: Client-Server! O Cliente e o Servidor podem ser substituídos livremente e nenhuma suposição deve ser feita sobre suas implementações.

Você recebe e retorna REPRESENTAÇÕES DE RECURSOS, e não registros de bancos de dados! E você deve retornar aquilo que for relevante para o caso de uso da API. Se houver mais dados e relacionamentos, eles podem ser retornados como relacionamentos extra-path:

  • GET /api/cliente/12: Recupera os dados básicos do cliente número 12;
  • GET /api/cliente: Recupera todos os clientes, podendo paginar;
  • GET /api/cliente/12/pedido: Recupera todos os pedidos do cliente 12;
  • GET /api/cliente/12/pedido/1500: Recupera os dados do pedido 1500 do cliente 12.
A representação de um recurso deve ser totalmente independente do armazenamento do mesmo. O Cliente não deve supor nada! Não precisa saber qual é a "chave primária" e quais são os relacionamentos! E nem deve receber os dados de TODAS as entidades JPA relacionadas!

Problemas de trafegar as entidades JPA

Para começar, quem lhe disse que o Cliente da sua API está interessado nos relacionamentos das suas entidades JPA? Quem lhe disse que é esse o modelo que ele vai seguir? E pior: Quem lhe disse que ele quer exatamente aquelas informações? 

Imagine um modelo assim: 



Como você faria para incluir um Pedido? Teria que passar os dados do Cliente? Se você tiver que alterar uma Fatura de uma Entrega, como faria?

A verdade é que o modelo JPA é "normalizado" em OOP, mapeando os relacionamentos como coleções de objetos, o que pode ser bom para aplicações CRUD, mas ruim para APIs REST.

Fora a questão dos identificadores... Por exemplo, sua chave primária de Cliente é uma "Sequence", mas o usuário conhece o Cliente pelo CNPJ.

Wrapper / Mapper

Uma prática que pode ajudar é "desORMizar" seu modelo de entidades, criando uma camada "wrapper". Classes que "embrulham" os dados das suas entidades, modelando-os para tornar a API o mais prática e independente possível.

E criamos classes "Mappers" para transformar "de/para" as entidades JPA.

Quer um exemplo? Vamos supor que 99% dos clientes da sua API querem ver apenas os seus dados básicos (CNPJ e Razão Social) e alguns dados do último pedido (Número da fatura, Data de entrega, Valor, Itens). Podemos criar um "Wrapper" só com isso.

E se o cliente quiser o resto dos pedidos? Criamos outro Wrapper!

Há várias maneiras de fazer isso. Se você usa Spring Boot, por exemplo, ESTE ARTIGO lhe ajudará bastante.

Nenhum comentário:

Postar um comentário