O que é gRPC - Como usar os Protocol Buffers | Parte 2
Tags: protocolbuffers, grpc, protobuf, rpc
Published: 2022-04-26 16:01:08

**Autor:** Rodrigo G. Tavares
**Veja a Parte 1:** [O que é gRPC - Seus componentes RPC e HTTP2](https://dev.to/expertostech/o-que-e-grpc-seus-componentes-rpc-e-http2-parte-1-nnm)

{% youtube rqXBSbN84lo %}

Protocol Buffers ou para os íntimos Protobuf, é uma linguagem neutra criada para permitir a integração entre linguagens de programação, também usado como IDL pelo sistema gRPC.

Veremos nesse artigo o que são os Protocol Buffers, como eles funcionam e como eles se integram com o sistema gRPC.

## Introdução

Quando falamos de integração entre linguagens, estamos trazendo o conceito de interoperabilidade, ou seja, a capacidade das aplicações e sistemas se comunicarem de maneira simples e fácil.

## O que são os Protocol Buffers?

É uma linguagem neutra de plataforma neutra, usada para definição de tipos de dados e funções, muito parecido com o formato JSON, porém menor e mais rápido. 

Como vimos no artigo anterior, ele é usado como IDL, linguagem para definição de interfaces, no sistema gRPC.

Por que eu preciso de uma linguagem neutra?
Uma linguagem neutra é uma linguagem simples, usada pra fazer a definição das interfaces e tipos usada nas integrações entre sistemas.

A partir dessa interface, cada linguagem que queira fazer uma integração deve interpretar o código escrito na IDL, gerando funções e tipos nos padrões da sua linguagem.

Essas interfaces geradas são chamadas de stubs e são usadas tanto pelo provedor de serviços, quanto pelo consumidor.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rt9v8944fzzv3nekcm9x.png)

O lado provedor usará as interfaces para efetivamente implementar as regras daquele serviço, enquanto o consumidor, como o próprio nome diz, vai usá-las pra consumir, ou seja, acessar os serviços.
E é exatamente esse o papel dos protocol buffers.

## Estrutura dos Protocol Buffers

Precisamos de atenção nos padrões e convenções de formatação do arquivo proto. 

Mas por que isso é importante? 
Usar o formato correto faz com que o processo de geração dos stubs siga as convenções de cada linguagem. 
Em resumo, seguindo o padrão dos protocol buffers, fará com que o código gerado também esteja no padrão da linguagem de destino.

### Configurações

Ao criar o arquivo protocol buffer, por convenção o nome do arquivo deve ser todo minúsculo com a extensão .proto.

Exemplo:
- pessoa.proto
- pessoafisica.proto
- pessoajuridica.proto

#### Sintaxe

Uma vez criado o arquivo, devemos atribuir a versão da sintaxe, que pode ser: proto2 ou proto3.

```protobuf
syntax = "proto3";
```

Desse ponto em diante focaremos na versão de sintaxe `proto3`, que é a versão mais recente, para mais detalhes referente a versão `proto2`, há um link para a documentação nas referências do artigo.

#### Pacote

Agora precisamos definir o pacote, essa é uma instrução opcional usada pra compor o nome da mensagem, essa configuração deixa o nome único e evitando conflitos com outros tipos de nome semelhante, ou seja, podemos ter tipos de nomes iguais, desde de que estes estejam em pacotes diferentes.

```protobuf
package expertostech;
```

A instrução package em Java, go e csharp é usada pra compor o código gerado, no caso do csharp o valor é atribuído ao `namespace`, já no Go e no Java é atribuído em uma propriedade de mesmo nome.

#### Importação

Depois temos a área de importação, onde você pode fazer referência pra outros tipos, como é o caso da definição de data hora que precisa ser importado. Você também pode importar seus próprios tipos como referência.

```protobuf
import "google/protobuf/timestamp.proto";
import "endereco.proto";
```

#### Opções adicionais

Vamos agora para as configurações específicas de cada linguagem.

Para o Java, as configurações são:
- `java_multiple_files`, se verdadeiro, indica que as classes serão geradas em arquivos separados.
- `java_outer_classname`, o nome da classe para geração.

```protobuf
option java_multiple_files = true;
option java_outer_classname = "PessoaProtos";
```

Se você quiser organizar seus arquivos proto em pacotes diferentes das suas classes, você pode usar as seguintes configurações:

- `java_package`, para o Java;
- `go_package`, para o Go;
- `csharp_namespace`, para o C#.

Essas três configurações sobrescrevem o valor do pacote, package ou namespace no c#, alterando os valores para geração das classes.

```protobuf
option java_package = "expertostech.tutorial.grpc";
option go_package = "expertostech/tutorial/grpc";
option csharp_namespace = "ExpertosTech.Tutorial.Grpc";
```

### Definição de tipos

Uma curiosidade, é que os tipos nos protocol buffers são chamados de `message`, justamente por que esses tipos são usados como "mensagem" de envio e respostas nas suas integrações entre sistemas.

#### Mensagem

Iniciamos a declaração cada tipo com a palavra chave `message`, seguido do nome no padrão CamelCase.

```protobuf
message Pessoa {
}
```

É importante saber que podemos declarar várias mensagens em um mesmo arquivo proto.

```protobuf
message Pessoa {
}

message Usuario {
}
```

#### Atributos

Agora vamos declarar os atributos da mensagem.

Cada atributo começa com o tipo, seguido do nome e ao fim um código de identificação único.

```protobuf
message Pessoa {
  string documento_pessoal = 1;
}
```

Esse código deve ser único na mensagem e não no arquivo proto, isso quer dizer que você pode ter vários identificadores de atributo com o número 1, por exemplo, desde que eles estejam em mensagens diferentes.

```protobuf
message Pessoa {
  string documento_pessoal = 1;
}

message Usuario {
  string login = 1;
}
```

Essa identificação deve ser feita a partir do número 1 e pode chegar até 536.870.911, ou (2^29)-1.

Eu tenho minhas dúvidas se você vai precisar chegar tão longe, mas é importante dizer que os números entre 19.000 à 19.999 são reservados para a identificação dos atributos do framework, isso quer dizer que se você usá-los, a geração dos stubs apresentará erro.

Nos atributos usamos como convenção de nome, letras minúsculas separando cada palavra com um underscore.

```protobuf
message Pessoa {
  string documento_pessoal = 1;
  string nome = 2;
}
```

#### Tipos de dados

Na tabela abaixo podemos ver os principais tipos para declaração de atributos e quais são os seus correspondentes nas principais linguagens. Caso queira ver a lista completa, há um link para a documentação nas referências do artigo.

| .proto Type   | C++ Type      | Java/Kotlin Type  |  C# Type  | Go Type |
| ------------- | ------------- | ----------------- | --------- | ------- |
| string        | string        | String            | string    | string  |
| int32         | int32         | int               | int       | int     |
| float         | float         | float             | float     | float64 |
| double        | double        | double            | double    | float32 |
| bool          | bool          | boolean           | bool      | bool    |

Além dos tipos de dados da linguagem, você também pode usar como tipos nos seus atributos suas próprias mensagens, ou seja, você pode por exemplo criar uma mensagem `Cidade` e usá-la como referência dentro da mensagem `Endereco`.

```protobuf
message Endereco {
  string logradouro = 1;
  string numero = 2;
  string bairro = 3;
  Cidade cidade = 4;
}

message Cidade {
  string nome = 1;
  int32 ddd = 2;
}
```

É importante ressaltar que caso o tipo utilizado localizado em outro arquivo proto, é necessário importá-lo na seção `import`.

```protobuf
// ...
import "endereco.proto";
// ...
message Pessoa {
  string documento_pessoal = 1;
  string nome = 2;
  Endereco endereco = 3;
}
```

#### Listas de valores

Um recurso muito utilizado nas integrações são as listas de valores e temos dois tipos de listas nos protocol buffers:

##### Listas simples

Para declarar uma lista usamos a palavra chave `repeated`, seguida pelo tipo do campo e o nome da lista.
Por convenção, uma vez que as listas possuem diversos itens, elas sempre são nomeadas no plural.

```protobuf
message Pessoa {
  repeated Endereco enderecos = 3;
}
```

##### Listas chave e valor

Outra lista que temos disponível é a de chave e valor, chamada de `map`, nesse tipo de lista podemos definir um tipo de dado para chave e outro tipo de dado para valor, lembrando que tanto na chave quanto no valor podemos usar tipos da linguagem ou nossos próprios tipos.

```protobuf
message Pessoa {
  map<string, google.protobuf.Timestamp> atualizacoes = 4;
}
```

#### Enumerações

Fechando os principais tipos da linguagem, também temos um conjunto de valores fixos chamados de `enum`.
Os enums têm a estrutura semelhante a de uma mensagem, porém, ao invés de atributos, há uma lista fixa de valores.

Pra declarar uma enumeração usamos a palavra chave `enum` seguida do nome no padrão CamelCase.
Para a lista de valores, usamos as letras todas maiúsculas separando as palavras por um underscore.

Os itens do `enum` também precisam ser numerados, só que diferente dos atributos da mensagem, a lista de valores do enum deve iniciar no número 0.

```protobuf
enum TipoPessoa {
  NAO_DEFINIDA = 0;
  FISICA = 1;
  JURIDICA = 2;
}
```

#### Serviços

Fechamos a definição dos tipos, agora precisamos definir nossos serviços e funções.

Isso é muito simples, usamos a palavra chave `service`, seguida do nome do serviço no padrão CamelCase.

```protobuf
service PessoaServico {
}
```

Para declararmos uma função dentro do seu serviço, iniciamos com a palavra chave `rpc`, seguida pelo o nome da funcionalidade no padrão CamelCase, o parâmetro de entrada entre parênteses, seguido da palavra chave `returns` com a mensagem de retorno da função também entre parênteses.

```protobuf
service PessoaServico {
  rpc PessoaPorDocumento(Pessoa) returns (Pessoa) {}
}
```

Podemos declarar várias funções dentro de um mesmo serviço e obrigatoriamente todas elas precisam de uma mensagem como parâmetro de entrada e uma outra como parâmetro de saída.

Não podemos ter funções sem entrada ou saída, e os parâmetros devem obrigatoriamente ser mensagens e não tipos simples, como strings, inteiros e etc.

```protobuf
service PessoaServico {
  rpc PessoaPorDocumento(Pessoa) returns (Pessoa) {}
  rpc PessoaPorNome(Pessoa) returns (Pessoa) {}
}
```

Vejamos abaixo o código completo dos protocol buffers usados até aqui como exemplo.

_Arquivo: endereco.proto_
```protobuf
syntax = "proto3";
package expertostech;

option java_multiple_files = true;
option java_outer_classname = "EnderecoProtos";

option java_package = "expertostech.tutorial.grpc";
option go_package = "expertostech/tutorial/grpc";
option csharp_namespace = "ExpertosTech.Tutorial.Grpc";

message Endereco {
  string logradouro = 1;
  string numero = 2;
  string bairro = 3;
  Cidade cidade = 4;
}

message Cidade {
  string nome = 1;
  int32 ddd = 2;
}
```

_Arquivo: pessoa.proto_
```protobuf
syntax = "proto3";
package expertostech;

import "google/protobuf/timestamp.proto";
import "endereco.proto";

option java_multiple_files = true;
option java_outer_classname = "PessoaProtos";

option java_package = "expertostech.tutorial.grpc";
option go_package = "expertostech/tutorial/grpc";
option csharp_namespace = "ExpertosTech.Tutorial.Grpc";

service PessoaServico {
  rpc PessoaPorDocumento(Pessoa) returns (Pessoa) {}
  rpc PessoaPorNome(Pessoa) returns (Pessoa) {}
}

message Pessoa {
  string documento_pessoal = 1;
  string nome = 2;
  repeated Endereco enderecos = 3;
  map<string, google.protobuf.Timestamp> atualizacoes = 4;
  TipoPessoa tipo_pessoa = 5;
}

enum TipoPessoa {
  NAO_DEFINIDA = 0;
  FISICA = 1;
  JURIDICA = 2;
}

message Usuario {
  string login = 1;
  string senha = 2;
}
```

## Gerando os stubs com Java

Para testar a geração veja o projeto completo no Github:
[github/expertos-tech/protocol-buffer](https://github.com/expertos-tech/protocol-buffer)

Veja abaixo a estrutura do projeto e arquivo `pom.xml`.

![Estrutura do projeto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bc38kh04hbo6uj5bushf.png)

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>expertostech</groupId>
    <artifactId>stub-gen</artifactId>
    <version>1.0-SNAPSHOT</version>


    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.45.1</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.45.1</version>
        </dependency>
        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>1.3.5</version>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.4.1.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
```

## O que vem a seguir?

Fechamos a definição dos protocol buffers, que é a parte central do sistema gRPC.
No próximo artigo, última parte dessa série, entraremos na parte prática de tudo que vimos até aqui, a implementação passo a passo de um serviço gRPC com Java, usando os protocol buffers.

E se você chegou até aqui, deixe o seu gostei no artigo e já aproveita pra seguir o canal ExpertosTech em todas redes sociais:
[https://linktr.ee/expertostech](https://linktr.ee/expertostech)

---
**Referencias:**

- [Documentação Prtocol Buffers](https://developers.google.com/protocol-buffers/docs/overview)