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 HTTPDescriçãoExemplo de UsoIdempotente?
GETObtém dados do servidor sem modificá-los.GET /usuarios → Retorna todos os usuários.✅ Sim
POSTCria um novo recurso no servidor.POST /usuarios → Cria um novo usuário.❌ Não
PUTAtualiza completamente um recurso existente.PUT /usuarios/1 → Substitui os dados do usuário com ID 1.✅ Sim
PATCHAtualiza parcialmente um recurso existente.PATCH /usuarios/1 → Modifica apenas um campo do usuário.❌ Não
DELETERemove um recurso do servidor.DELETE /usuarios/1 → Exclui o usuário com ID 1.❌ Não
OPTIONSRetorna os métodos HTTP suportados por um recurso.OPTIONS /usuarios → Retorna GET, POST, PUT, DELETE.✅ Sim
HEADIgual 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