O estilo arquitetural REST anda de mãos dadas com o protocolo HTTP, porém não se limita a ele, contudo trataremos sobre REST, HTTP e JSON em nossos exemplos por se tratar de uma combinação comum.
Não irei traçar comparações entre REST e SOAP e muito menos contar a história da REST e do protocolo HTTP, pois esse conteúdo é abundante na internet. O propósito desse artigo é aplicar e passar pelos níveis de maturidade, entendendo os problemas que eles resolvem, e apresentando exemplos intuitivos.
O modelo de Leonard Richardson divide a implementação em 4 níveis de maturidade, partindo do nível zero e indo até o nível 3. Iremos elaborar um problema e assim aplicaremos cada um dos níveis, dessa forma ficará claro quais os problemas que cada nível resolve.
Segundo Roy Fielding (criador do conceito REST) sua API só pode ser chamada de REST API quando for HATEOAS, ou seja, quando estiver no terceiro nível de maturidade.
"O que é necessário para tornar o estilo de arquitetura REST claro sobre a noção de que o hipertexto é uma restrição? Em outras palavras, se o mecanismo do estado do aplicativo (e, portanto, a API) não estiver sendo controlada pelo hipertexto, ela não poderá ser RESTful e não poderá ser uma API REST."
Tendo essa premissa, iremos resolver o problema ao ponto que nossa API possa ser considera uma REST API. Problema:
Figura 1
Os quadrados definem o agrupamento de informação no caso, customers, orders, products etc. E as linhas definem o tipo de relação que um tem com o outro; esses são os pontos essenciais e que devem estar representados em uma API.
Nível 0 de maturidade
Nesse nível tende-se a usar somente o POST como verbo e utilizar apenas uma URI, combinando com uma variedade de comandos próprios.
A api do Flickr é um exemplo clássico desse nível. Por este motivo a necessidade de uma documentação se torna emergente visto que os comandos não são padrões, os status de retornos também não são utilizados, com isso se perde o poder da predição, onde conhecendo o estilo e o protocolo, tem-se um entendimento base do funcionamento da API.
Veja um exemplo:
POST http://mycompany.com?list.products
POST http://mycompany.com?create.product
POST http://mycompany.com?delete.order=1
...
É interessante observar que para operações simples se faz necessário uma documentação robusta, pois além da falta de clareza sobre a estrutura e suas relações, os comandos são personalizados, como por ex.: list.products, create.product etc.
Padrões internos possui problemas como a falta de documentação ou documentação desatualizada ao ponto que se perde o conhecimentos sobre a API se os idealizadores não fizerem mais parte do projeto. No final sobrará somente o código fonte e algumas horas perdidas para tentar entender o que foi pensado.
Documentar é um processo fundamental, porém somente o necessário; se você utilizar o HTTP como especificado, não será necessário documentar algo que já está documentado, e o mesmo se aplica ao REST.
Nível 1 de maturidade - Recursos
Esse é o nível que demanda mais atenção, em minha opinião. O recurso é uma forma de organizar conjuntos de informações coesas. Ele delimita fronteiras entre um tema e o outro. Essa é o "Gênesis da API", pois é onde tudo começa.
Roy Fielding define recurso da seguinte forma:
"A abstração chave de informação no REST é um recurso. Qualquer informação que possa receber um nome pode ser um recurso: um documento, uma imagem, um serviço (por exemplo, "clima de hoje em Los Angeles"), um objeto não virtual (uma pessoa, por exemplo), uma coleção de outros recursos e assim por diante. Em outras palavras, qualquer conceito que possa ser alvo de uma referência deve se encaixar na definição de recurso."
Entende-se que qualquer conceito que possa ser alcançado a partir de um link pode ser modelado como recurso. A grosso modo seria como modelar as tabelas em um banco relacional.
Cada recurso deve ter uma estrutura e uma URI que será usada para identificá-lo. Recursos não devem ser uma cópia exata de suas tabelas no banco de dados ou do seu modelo de objetos, ele deve ser a forma como você quer que o cliente entenda sua estrutura de informação, independente da técnica aplicada no backend. Entendido isso, vamos implementar a API.
No exemplo abaixo temos uma URI que identifica o recurso "products", e esse recurso é composto pelo id, nome e valor.
POST http://mycompany.com/products/1
Reposta:
Content-Type: application/json
{
"id": 1,
"name": "MacBook Pro",
"price": 10.000
}
Assim teremos uma URI para cada recurso da nossa API, como no ex.:
POST /customers/1
POST /customers/1/orders
POST /customers/orders/1/order-items
POST /products/1
POST /packages/1
O verbo, a exemplo do nível zero, continua sendo o POST.
Nome dos recursos no plural
Manter o nome do recurso no plural é uma boa prática. Isso se justifica se pensarmos em recursos como pensamos em diretórios, e assim podemos dizer que o diretório customers tem um arquivo com uma estrutura composta por id, nome e sexo e o identificador(nome do arquivo) é 2, por ex.:
Figura 2
Ao buscar um cliente:
POST /customers/2(diretório clientes e arquivo 2)
Resposta:
Content-Type: application/json
{
"id": 2,
"name": "Adam Smith",
"gender":"M"
}
Fica claro que ao chamar /customers irá retornar todos os recursos (arquivos) de dentro do diretório clientes.
Recurso raiz
Um produto, assim como cliente, é um recurso raiz. Um recurso raiz existe independente de outro recurso. Ele aparecerá após o domínio da aplicação.
POST http://mycompany.com/customers
POST http://mycompany.com/products
POST http://mycompany.com/packages
...
Isso significa que posso criar um cliente, produto ou pacote independente de qualquer outro recurso.
Sub recurso - Hierarquia
Um sub recurso existirá após a criação do seu antecessor. Pedidos não existe sem um cliente associado a ele, ou seja, o recurso pedidos vive em função de um cliente e por esse motivo pedidos deve estar abaixo de cliente para evidenciar esse fato.
Figura 3
Baseado na imagem acima podemos fazer uma solicitação para recuperar todos os pedidos do cliente 2:
POST /customers/2/orders
Resultado:
HTTP/1.1 200 OK Content-Type: application/json
[{
"id": 20,
"total": "10.000",
},
{
"id": 23,
"total": "2.000",
}
]
Dessa forma fica evidente que não existirá, no sistema, um pedido que não esteja associado a um cliente.
O mesmo recurso não pode aparecer em lugares distintos
Um recurso não pode aparecer em lugares distintos com o mesmo nome, pois isso traria, uma certa nebulosidade sobre a API.
No nosso problema temos produtos e pacote de produtos, em um pacote podemos ter vários produtos, para vendê-los como combo. Agora queremos saber quais os produtos estão em um pacote. Baseado no que vimos até aqui talvez isso fizesse sentido:
POST /packages/2/products
Porém não podemos fazer isso porque produtos é um recurso que já existe em outro ponto da API.
Assim como usamos os substantivos para qualificar um agrupamento de informação (produtos, clientes, etc) podemos fazer o mesmo para as relações.
Qualificando relações
Toda vez que uma relação estiver em evidência, por exemplo: for necessário executar operações, ou for acessível por um link (nível 3 de maturidade), essa relação deverá ser representada.
Para adicionar um produto em um pacote a operação não será sobre o produto, nem sobre o pacote e sim sobre a relação que existe entre os dois.
O exemplo abaixo mostra a representação dessa relação:
POST /packages/1/products-relationships
Resultado:
HTTP/1.1 200 OK Content-Type: application/json
[{
"id": 1,
"productId":2
},
{
"id": 2,
"productId":3
}
]
Nesse caso o pacote 1 está relacionado ao produto 2 e 3. Ainda não está bom porque não temos ferramentas suficientes nesse nível. Iremos melhorar isso no nível 2 e 3.
Assim como em um banco relacional onde você precisa de uma tabela intermediária para ligar duas entidades, no caso de pacotes e produtos, com REST você também precisará de um recurso intermediário para relacioná-los. Entretanto isso só será modelado dessa forma se você precisar executar operações sobre a relação, caso contrário é desnecessário.
O "products-relationships" não precisa ser mais uma tabela no banco ou mais um objeto no domínio porque esses dois mundos têm suas próprias regras.
Qualificando operações
Existem situações onde podemos abstrair uma complexidade de negócio para não ter regras distribuídas pelos clientes que consomem a API.
Imagine que que ao alterar o preço de um pacote eu tenha que enviar um e-mail, notificando o departamento de vendas e solicitar a aprovação do novo preço.
O cliente da API poderia ser informado que, após alterar o preço do pacote, deve enviar um e-mail para o departamento de vendas e solicitar a aprovação do novo preço - toda vez que essa alteração ocorrer.
O problema dessa abordagem é que quando a regra de negócio mudar (ex.: deve enviar um e-mail para o departamento de compras), todos os clientes da API deverão ser alterados para implementar a nova regra de negócio; imagine com 10 clientes ou mais, todos eles deverão ser alterados para a nova regra, isso costuma ser custoso e demorado.
O outro ponto está relacionado a informações adicionais como por ex.: quem aprovou a alteração do preço do pacote?
Para resolver esse problema e evitar entregar a regra de negócio para o cliente da API podemos representar esse conceito em forma de recurso, da seguinte forma:
POST /packages/1/change-of-prices
Resultado:
HTTP/1.1 200 OK Content-Type: application/json
[
{
"id":2,
"value":150.00,
"status": "PENDING",
"requestDate": "20/08/2018"
},
{
"id":1,
"value":200.00,
"status": "APPROVED",
"requestDate": "10/05/2018",
"comments": "approved for the mother's day campaign"
"approverId": 20
}
]
Nesse caso temos duas solicitações de alteração de preço para o pacote 1. A alteração solicitada em 10/05/2018, foi aprovada pelo aprovador identificado pelo id 20, e também tem uma justificativa da aprovação.
A solicitação do dia 12/08/2018, ainda está pendente de aprovação. Dessa forma conseguimos manter a regra no backend, a operação não precisa ser síncrona e mantemos um histórico de alterações de preços (esse modelo é muito aderente ao padrão CQRS).
Com uma representação do processo consigo adicionar informações relevantes como comentário, aprovador e o que mais for necessário.
É sempre importante pensar sobre o uso do PUT e do PATCH (nível 2) existem alterações que são mais complexas do que somente alterar o valor, como exposto no exemplo anterior.
Obs: como não temos links hypermedia nesse nível, o "approverId" foi colocado no corpo do recurso, iremos resolver isso quando falarmos de HATEOAS.
Nomeando os recursos
O ponto mais importante aqui é a consistência, use o mesmo padrão para toda a API. A RFC define a formação de uma URI. O mais importante para nós segue abaixo:
POST /customers/2/orders (sem letras maiúsculas)
POST /products/2/basic-categories (use o hífen para separações)
Recurso como operação existente
É comum aparecer nesse nível recursos como operações que já existem no protocolo. Ex.:
POST /customers/2/update
POST /customers/create
POST /customers/2/delete
...
Essa estrutura não está representando um update ou create, está sendo usado como um verbo. Esses verbos já existem no protocolo HTTP, então não faça isso.
Recurso como agente transformador
Essa é outra forma equivocada do uso dos recursos. Usá-los para transformar dados não é a forma adequada para tal finalidade. Ex.:
POST /customers/2/xml
POST /customers/2/json
...
Existe à maneira REST de negociar o formato dos dados e isso pode ser feito usando content negotiation.
Extensão como agente transformador
É comum alguns frameworks e desenvolvedores usarem extensões para indicar o tipo do dado que deverá ser retornado.
POST http://mycompany.com/customers/2.json
POST http://mycompany.com/customers.xml
...
Essa não é a forma adequada para esse tipo de operação. Como na solução anterior o content negotiation poderia ser usado.
Finalizamos o nível 1 de maturidade, com isso organizamos os dados e adicionamos um nível de relacionamento entre eles (hierarquias). Com tudo organizado podemos entender quais operações poderão ser executada sobre os recursos e a relação entre eles nos próximos tópicos.
Nível 2 de maturidade - Verbos HTTP
Esse nível apresenta um conjunto de verbos que representam operações possíveis sobre um recurso, no primeiro nível definimos os substantivos e nesse nível iremos aplicar os verbos. Os principais verbos HTTP são: GET, PUT, POST, DELETE, HEAD, PATCH, OPTIONS.
No nosso sistema de vendas, entendendo que temos o recurso clientes, préviamente sabemos que podemos alterar, ler, remover, criar um recurso. Fazendo uma analogia com um banco de dados relacional seriam as operações que podem ser executadas sobre um tabela, como select, update, delete etc.
POST /customers (criar)
GET /customers/20 (buscar)
PUT /customers/20 (atualizar)
PATCH /customers/20 (atualizar parcialmente)
DELETE /customers/20 (remover)
HEAD /customers/20 (verificar se existe)
OPTIONS /customers/20 (operações possíveis)
Não irei explicar cada método e seus respectivos retornos HTTP, porque esse conteúdo há em abundância na internet, porém farei algumas observações que considero relevantes.
PUT ou PATCH? A forma de uso do verbo PUT é sempre enviando todos os atributos no payload, no caso do recurso "customers" seria necessário enviar sexo, mesmo querendo alterar somente o nome. O contraponto seria o PATCH onde você envia somente a informação que quer alterar.
"Patch foi algo que criei para a proposta inicial do HTTP/1.1 porque PUT parcial não é RESTful "
HEAD: Esse verbo é um GET sem o corpo, ele é muito útil quando queremos verificar se um recurso existe sem ter o custo da latência de rede no transporte do corpo da mensagem.
GET: Existem questionamentos interessantes sobre qual status HTTP retornar quando uma coleção estiver vazia. Ex.:
GET http://mycompany.com/customers/20/orders
A idéia é que a coleção existe (diretório pedidos) independente de não haver conteúdo, por esse motivo é comum API's retornarem 200 OK.
Faz sentido porque o seu hypermedia (nível 3) poderia retornar informações sobre o diretório como a data da última atualização, por exemplo. Olha o que o Roy Fielding responde ao ser questionado sobre esse tema:
"... REST é diferente do HTTP. 200 significa que a solicitação foi satisfeita; 404 significa que o servidor não tem nesse momento a representação do recurso associada à URI da requisição; use o que você quiser dizer ao cliente."
OPTIONS: Esse verbo é uma ótima ferramenta para identificar quais operações podem ser executadas sobre um recurso.
Os verbos HTTP deveriam ser suficientes para um CRUD
Para entender essa sessão é importante ler "Qualificando relações", irei usar o mesmo exemplo.
Vamos analisar um cenário onde a relação entre pacotes e produtos não é representada, nesse cenário qual verbo eu utilizaria para adicionaria um produto a um pacote?
Se for usado o POST na URI: /packages/20, iria passar a intenção de que estamos criando um pacote.
Se substituirmos o POST por um PUT ou PATCH estamos informando que o pacote está sendo atualizado.
Dessa forma não fica claro a intenção de associar um produto a um pacote, por esse motivo a representação da relação se fez necessária porque agora podemos fazer o seguinte:
Cria a relação entre o pacote 2 e o produto informado no payload:
POST /packages/2/products-relationships
Recupera todas a relações entre o pacote 2 e os produtos:
GET /packages/2/products-relationships
Remove uma relação (tira o produto do pacote):
DELETE /packages/products-relationships/1
...
Esse tipo de problema tende a gerar soluções RPC como sugerido na RFC 6902; ela propõe adicionar os comandos test, remove, add, replace, move e copy sobre o PATCH. Isso é uma anomalia que foi gerada pela falta de representação de relações ou operações.
Metadados melhoram o entendimento
Às vezes um recurso tem mais informações do que foi enviado ao criá-lo, ex.:
POST /products/1
corpo:
{
"name": "MacBook Pro",
"price": 10.000,
}
Reposta:
Content-Type: application/json
{
"id": 1,
"name": "MacBook Pro",
"price": 10.000,
"creation": "05/03/2018",
"lastUpdate": "23/06/2018"
}
Os atributos "creation" e "lastUpdate" são gerenciados pelo servidor, dessa forma não posso criar um produto enviando esses atributos, provavelmente tomaria um "bad request".
Vamos supor que o recurso "products" suporte os verbos POST, PUT e PATCH, entretanto isso não seria verdade para os atributos "creation" e "lastUpdate". Se fosse uma imagem teríamos o tamanho, dimensões etc.
Para resolver isso podemos separar esses atributos e deixá-los em outro recurso, responsável por gerenciar as meta informações, pois o cliente não pode alterá-las, ex.:
GET /products/1/product-metadatas
Reposta:
Content-Type: application/json
{
"creation": "05/03/2018",
"lastUpdate": "23/06/2018"
}
A URI: /products/1/product-metadatas, suporta somente o GET, assim o cliente saberá que é somente leitura.
Por sua vez o produto ficará assim:
GET /products/1
Reposta:
Content-Type: application/json
{
"id": 1,
"name": "MacBook Pro",
"price": 10.000,
}
Agora o recurso tem somente os atributos que aceitam os verbos suportados pela uri: /products/{id}.
Imagino que você tenha pesando: e o id? O id também é um atributo que não pode ser atualizado ou enviado na criação do recurso, nesses casos o recomendado seria ter o id informado no link "self" (nível 3). Implementarei isso no próximo nível, com exemplos para ficar um pouco mais claro.
Finalizamos o nível 2 e esse nível ajudou a adicionar um entendimento prévio sobre as operações que posso executar sobre um recurso. Ele também ajudou a identificar problemas de modelagem que ocorre quando usamos o verbo com propósito distinto da definição.
Nível 3 de maturidade - HATEOAS
HATEOAS (Hypermedia As The Engine Of Application State ) basicamente são links e aplicação de semântica usando media-types.
A base do estilo arquitetural REST é a WEB (HTTP + HTML) e para entender isso irei fazer uma analogia entre os níveis e a WEB.
O globo seria a nossa api e os sites são os recursos. No caso do InfoQ, as páginas development, architecture-design e culture-methods são sub recursos.
Imagine entrar no site do InfoQ e não ter links para as páginas, talvez você tivesse que mandar um e-mail para alguém perguntando quais as páginas existentes no site. Isso traria uma complexidade sem igual e tornaria a navegação precária.
Por esse motivo o HTML + HTTP são os padrões de hypermedia e semântica para a WEB. Tem os links para prover a ligação entre as páginas e recursos de mídia e media-types que definem o tipo da informação.
Essa analogia é para mostrar que, assim como o HTML+HTTP é o padrão para os seres humanos o HATEOAS é para as máquinas, e para os seres humanos desenvolvedores.
Antes dos exemplos precisamos de um hypermedia, e para isso usaremos o HAL com implementação em JSON, ele é um tipo de HTML das máquinas. Ele não é só um JSON, ele é um JSON estruturado, com definições de como o dado deve ser apresentado; como links, listas, paginação etc.
Para mais detalhes sobre o HAL você pode encontrar aqui
Para não deixar pontas soltas vamos resolver o problema do recurso "packages". Vamos imaginar que o HAL foi adicionado a todos os recursos do nosso problema.
Ao buscar um pacote, o retorno usando o HAL seria:
GET http://mycompany.com/packages/2
Resultado:
HTTP/1.1 200 Content-Type: application/vnd.hal+json
{
"name": "Combo Upgrade PC",
"price": 500.00,
"_links": {
"self": {
"href": "/packages/2"
},
"curies": [
{
"name": "doc",
"href": "http://mycompany.com/docs/{rel}",
"templated": true
}
],
"has-products": {
"href": "/packages/2/products-relationships"
},
"publish-to-adsense": {
"href": "/adsense/7/for-packages
}
}
}
Quando for chamado um recurso usando o ID a única coisa que irá variar entre os recursos serão os atributos, nesse caso name e price. A estrutura de links sempre terá o "self" que irá colocar a URI (id do recurso) que foi chamada pelo cliente. Os demais links serão as ações e relacionamentos que o recurso tem.
Se esse recurso fosse uma página HTML ela seria parecida com a imagem abaixo:
- O breadcrumb seria o link self;
- Os campos de textos ficam os dados do recurso;
- Eu posso ligar ou desligar o AdSense pelo rel: publish-to-adsense;
- Eu posso listar os produtos que esse pacote tem, caso não tenha o link, é presumível que o pacote esteja vazio.
- Com o rel "curies" eu posso colocar links para a documentação do recurso, no caso foi enviado um link template. Para o cliente (desenvolvedor) usar é só trocar o {rel} por "packages" ou "publish-to-adsense" ou "has-products" isso depende da estratégia que você irá adotar.
A tela HTML foi mostrada apenas para ilustrar como seria a versão para humanos de um recurso usando HATEOAS (links/content-type). Esse mesmo exemplo com um JSON sem HATEOAS teria somente os campos id, nome, e preço, bem pobre de informações.
Para montar a tela de exemplo seria necessário algumas requisições, pois precisaria pegar o link e buscar os produtos para assim listar o nome deles e não só os ids. Para os AdSense, seria necessário fazer uma requisição para verificar se o cadastro já foi feito e assim marcar se está ativo ou não.
Dessa forma os recursos se tornam dinâmicos e fácil de interpretar. Não preciso ligar para o desenvolvedor da API para perguntar como adicionar um pacote no AdSense ou como recuperar os produtos de um pacote, essa informação pode ser colocado como hypermedia, assim como os links são usado em uma página HTML para informar ao usuário o que ele pode fazer.
Os links são todas as ações possíveis sobre o recurso, se não estiver descrito é porque não tem.
Esse é um assunto um pouco mais longo e irei terminá-lo em outro artigo mas fica essa prévia do que irei abordar lá.
Exemplos anteriores com HATEOAS aplicado
Relacionamento entre produtos e pacotes, abordado em: "Qualificando relações".
GET /packages/1/products-relationships
Resultado:
HTTP/1.1 200 Content-Type: application/vnd.hal+json
{
"_embbeded": {
{
"_links": {
"self": {
"href": "/packages/products-relationships/1"
},
"product": {
"href": "/products/2"
},
"package": {
"href": "/packages/1
}
}
},
{
"_links": {
"self": {
"href": "/packages/products-relationships/2"
},
"product": {
"href": "/products/3"
},
"package": {
"href": "/packages/1
}
}
}
}
}
O pacote 1 está relacionado aos produtos 2 e 3.
Alteração de preços abordado em: "Qualificando operações"
GET /packages/1/change-of-prices
Resultado:
HTTP/1.1 200 Content-Type: application/vnd.hal+json
{
"_embbeded": {
{
{
"value":150.00,
"status": "PENDING",
"requestDate": "05/08/2018"
},
"_links": {
"self": {
"href": "/packages/change-of-prices/2"
},
"package": {
"href": "/packages/1
}
}
},
{
"value":200.00,
"status": "APPROVED",
"requestDate": "10/05/2018",
"comments": "approved for the mother's day campaign",
"_links": {
"self": {
"href": "/packages/change-of-prices/1"
},
"package": {
"href": "/packages/1
},
"who-approved": {
"href": "/approvers/20
}
}
}
}
}
Usando metadados, abordado em: "Metadados melhoram o entendimento"
GET http://mycompany.com/products/1
Resultado:
HTTP/1.1 200 Content-Type: application/vnd.hal+json
{
"name": "MacBook Pro",
"price": 10.000,
"_links": {
"self": {
"href": "/products/1"
},
"has-metadata": {
"href": "/products/1/product-metadatas"
},
"in-packages": {
"href": "/packages;id=3;id=8
}
}
}
Obs1.: O ID foi suprimido do payload, pois o link self faz o papel do identificador, isso trás mais semântica pois o ID de um recurso é uma URI, não um atributo no payload, se formos ser mais puristas. O Spring HATEOAS força o uso do ID no link self, assim no payload fica somente os atributos que suportam as operações da URI. Se a URI de produto suporta GET, PUT e POST, todos os atributos do payload suportarão esses métodos.
Obs2.: No link "in-packages" estou usando um recurso chamado Matrix Param, irei abordar em outro artigo onde falarei sobre filtros em uma REST API.
Conclusão
Uma API no nível 3 de maturidade fornece auto documentação, facilidade de entendimento e reaproveitamento. Em uma API bem modelada um sendbox já seria o suficiente para o cliente da API ir navegando e descobrindo os recursos possíveis.
Em muitos casos o cliente da API terá que fazer mais de uma solicitação para recuperar os dados que ele precisa e em outros casos virão mais informações do que o cliente precisa (como os links) isso é chamado de under-fetching e over-fetching respectivamente.
Não existe uma arquitetura com somente pontos positivos e que resolverá todos os problemas. Já vi muitas APIs com centenas de endpoints (RPC) que retornavam as mesmas coisas em URIs distintas e para clientes distintos, o custo de manutenção disso é exageradamente alto e o reaproveitamento baixo.
Ferramentas como GraphQL ajudam a resolver boa parte dos efeitos colaterais de uma API bem modelada. Irei falar sobre o GraphQL em um outro momento, pois é uma ferramenta que abstrai muito a complexidade de consumo de uma API de alto nível.
Fico por aqui e até a próxima.