Interfaces
Sommerville (2019), ao apresentar as atividades comuns a um processo de desenvolvimento de software, descreve como segunda atividade aquela denominada Desenvolvimento (ou Projeto e Implementação). Nesta atividade, há quatro artefatos importantes:
- o projeto de arquitetura, que trata dos componentes do sistema e de sua organização;
- o projeto de banco de dados, que compreende as estruturas de dados do sistema e a persistência dos dados em um sistema de gerenciamento de banco de dados, conforme determinado modelo;
- o projeto de interface, que aborda como ocorre a comunicação dos componentes de um sistema, bem como a comunicação entre diferentes aplicações que integram uma solução;
- a seleção e projeto de componentes, em que se busca encontrar componentes reusáveis e, caso não se encontre, projetam-se novos componentes.
Neste ponto, vamos nos atentar ao projeto de interface.
Conceito
📞 Soluções para o projeto de interface
Um dos aspectos mais interessantes que podemos observar na área de Computação é o fato de que há diversos padrões (ou protocolos) para problemas comuns. Como você já deve ter suposto, o problema de comunicação entre diferentes sistemas é recorrente. Portanto, há soluções ou estratégias que podem ser aplicadas para tal finalidade.
✉️ SOAP
O SOAP (Simple Object Access Protocol) é um protocolo de comunicação entre diferentes aplicações que utiliza o formato XML (Extensible Markup Language) para o intercâmbio de dados. Este é um critério bastante característico do SOAP: o uso de XML é obrigatório. Por outro lado, há uma flexibilidade interessante: a comunicação entre aplicações via SOAP pode ocorrer com o uso de diferentes protocolos, como HTTP e SMTP. No caso do HTTP, as requisições são enviadas com com o método POST,
pois GET
não permite enviar um corpo de requisição XML completo.
Em sua utilização, cada mensagem é enviada em um “envelope", que contém as seguintes informações:
- Header (Opcional): Contém metadados e informações adicionais, como autenticação e roteamento.
- Body: Contém a carga útil da mensagem SOAP.
- Fault (Opcional): Se houver erros no processamento, essa seção contém informações sobre a falha.
A figura a seguir representa visualmente a estrutura de um envelope.
block-beta
block
columns 1
a["Header (opcional)"] b["Body"] c["Fault"]
end
No W3Schools, há alguns exemplos com relação ao uso de SOAP.
<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Header>
...
</soap:Header>
<soap:Body>
...
<soap:Fault>
...
</soap:Fault>
</soap:Body>
</soap:Envelope>
Embora o protocolo SOAP seja consolidado e tenha possibilitado a construção de diversas APIs, o seu uso tem caído ao longo do tempo. Um dos fatores que contribuiu para isso consiste na popularidade do REST, que possibilita maior flexibilidade e o tráfego de mensagens com um custo de armazenamento reduzido (já que o XML implica em sobrecarga de informações).
🌐 REST
O REST (Representational State Transfer) é um estilo arquitetural que possibilita a comunicação entre diferentes aplicações, principalmente por meio do protocolo HTTP. No site da RedHat, ao apresentarem o conceito de REST, destacam que “REST (ou RESTful) é um conjunto de restrições de arquitetura, e não um protocolo ou padrão. Os desenvolvedores de API podem implementar o REST de diversas maneiras".
Enquanto o protocolo SOAP limita a troca de informações ao formato XML, uma API desenvolvida seguindo os princípios REST pode adotar diferentes formatos, mas o mais comum é o formato JSON (Javascript Object Notation), aceito por diferentes linguagens e que é facilmente legível por seres humanos.
{
"chave": "valor"
}
Além do tráfego de dados usualmente com o formato JSON, APIs REST adotam um conjunto de endpoints para receber e compartilhar informações.
No exemplo acima, foi utilizada a URL completa, incluindo o domínio. Quando se apresenta apenas o caminho interno, como /usuarios/1
, usa-se o termo URI (Uniform Resource Identifier).
Outro aspecto importante com relação à troca de dados com o uso de REST envolve o uso dos diferentes métodos HTTP, extrapolando os tradicionais GET
e POST
. A tabela a seguir apresenta os tipos de métodos HTTP e suas aplicações:
Método HTTP | Descrição | Exemplo de Uso | Idempotente? |
---|---|---|---|
GET | Obtém dados do servidor sem modificá-los. | GET /usuarios → Retorna todos os usuários. | ✅ Sim |
POST | Cria um novo recurso no servidor. | POST /usuarios → Cria um novo usuário. | ❌ Não |
PUT | Atualiza completamente um recurso existente. | PUT /usuarios/1 → Substitui os dados do usuário com ID 1. | ✅ Sim |
PATCH | Atualiza parcialmente um recurso existente. | PATCH /usuarios/1 → Modifica apenas um campo do usuário. | ❌ Não |
DELETE | Remove um recurso do servidor. | DELETE /usuarios/1 → Exclui o usuário com ID 1. | ❌ Não |
OPTIONS | Retorna os métodos HTTP suportados por um recurso. | OPTIONS /usuarios → Retorna GET, POST, PUT, DELETE . | ✅ Sim |
HEAD | Igual ao GET , mas sem o corpo da resposta. | HEAD /usuarios → Verifica se o recurso existe. | ✅ Sim |
Com o uso de REST, podemos implementar as operações de processamento de dados de um sistema em uma linguagem de programação (como PHP, por exemplo) e desenvolver diferentes aplicações de front-end (uma para web, um para dispositivo móvel e outra para TV), todas consumindo a mesma API.
Se você ficou confuso(a) com a diferença entre REST e SOAP, recomendo assistir ao vídeo a seguir:
https://www.youtube.com/watch?v=YWPT2UOxbUg
RESTful
Até aqui, conhecemos algumas características do estilo REST. Se você já tem pesquisado sobre o desenvolvimento de APIs ou de estratégias de comunicação entre diferentes aplicações, certamente já ouviu falar do conceito de RESTful. Em linhas gerais, uma API é considera RESTful quando atende a alguns critérios do estilo REST.
Esses critérios são:
🖥️ gRPC
Como vimos até aqui, o estilo REST traz mais flexibilidade para a comunicação entre aplicações que o protocolo SOAP. Sem dúvida alguma, a sua adoção é bastante vantajosa e possibilitou diversos avanços no desenvolvimento de aplicações ao longo dos últimos anos. Entretanto, não podemos ignorar algumas atividades envolvidas no processo de comunicação mediada pelo REST:
- O cliente precisa transformar os seus dados (como uma lista de objetos em Java) para o formato JSON;
- O cliente envia a requisição para o servidor por meio de um método HTTP;
- A requisição trafegará pela rede e ficará em situação “pendente” enquanto o servidor não responder (ou até atingir o timeout);
- Quando uma resposta é obtida, os dados precisam passar pelo processo de desserialização (ou seja, serem convertidos de JSON para o formato da linguagem usada no cliente).
Com o intuito de minimizar essas etapas, o Google propôs um protocolo chamado gRPC (Google Remote Procedure Call). Em linhas gerais, tal recurso é especialmente interessante para a implementação da comunicação de programas que executam em back-end, enquanto soluções baseadas em REST ainda são preferíveis para tratar a comunicação de front-end com back-end.
Na prática, o gRPC acaba “mascarando” as chamadas externas (por abstrair as camadas de rede), possibilitando que desenvolvedores de uma aplicação X acessem recursos de uma aplicação Y como se estivessem fazendo chamadas locais. Ou seja: não há necessidade de se efetuar, de forma explícita, o processo de serialização e desserialização.
Há, no entanto, algum “pedágio” para isso: o uso de protobuffer (protocol buffer).
Entendendo o ProtoBuf
O protobuf (protocol buffer) é um arquivo fundamental para o uso do gRPC, com a extensão .proto
. Ele define as estruturas de dados que são utilizadas na comunicação entre as diferentes aplicações e deve ser inserido tanto na aplicação servidor quanto na aplicação cliente. O próprio gRPC trata da serialização quanto da desserialização para uma linguagem.
Vejamos o seguinte exemplo de uma classe definida em TypeScript:
export class Pessoa{
nome: string;
email: string;
cpf: number;
}
Para possibilitar o compartilhamento de objetos dessa classe via gRPC, o arquivo deve conter:
syntax = "proto3";
message Pessoa {
string nome = 1;
string email = 2;
int32 cpf = number;
}
message ListaDePessoas {
repeated Pessoa = 1;
}
A grande vantagem é que o Protobuf utiliza um formato binário altamente otimizado, o que o torna mais rápido e menor em comparação com outros formatos de serialização, como XML ou JSON. Isso é importante especialmente em ambientes de rede, onde a largura de banda e o tempo de resposta são críticos.
Além do formato dos dados, o Protobuf também deve explicitar os serviços oferecidos via gRPC por aquela aplicação, apresentando o nome do método, o tipo de dado recebido como parâmetro e o tipo de retorno.
service PessoaService {
rpc ObterPessoas (google.protobuf.Empty) returns (ListaDePessoas);
rpc AdicionarPessoa (Pessoa) returns (google.protobuf.Empty);
}
Nesse exemplo:
- O serviço
PessoaService
oferece dois métodos:ObterPessoas
: Retorna uma lista de pessoas.AdicionarPessoa
: Recebe uma pessoa e retorna um status vazio (indicando sucesso ou falha na operação).
Note que o tipo google.protobuf.Empty
é um tipo especial usado quando não é necessário enviar nenhum dado na requisição ou na resposta, algo que pode ser útil para métodos que não requerem parâmetros ou retorno.
🤖 Configurações no servidor
Note que, até o momento, foram apenas definidos os tipos de dados. Para expor esses serviços no servidor e torná-los acessíveis via gRPC, será necessário implementar as funções de cada método definido no .proto
e, em seguida, registrá-las com o servidor gRPC. Esse processo, naturalmente, varia conforme a linguagem de prograação escolhida.
👩🏾💻 Configurações no cliente
No lado cliente, utilizaremos mesmo arquivo .proto
para gerar o código necessário para a comunicação com o servidor gRPC. Também será necessário tratar eventuais erros e definir uma função callback, para que os dados recebidos sejam processados na aplicação local.
👨🏾💻 Mãos à obra!
📚 Referências bibliográficas
- https://developer.mozilla.org/pt-BR/docs/Glossary/REST
- Sommerville, Ian. Engenharia de software (Portuguese Edition) (p. 42).