Projete boas APIs com Java exceptions
Tags: tutorial, braziliandevs, ptbr, java
Published: 2021-06-18 12:56:00

---
title: Projete boas APIs com Java exceptions 
published: true
description: Projete boas APIs com Java exceptions 
tags: tutorial, braziliandevs, ptbr, java 
cover_image: https://apexensino.com.br/wp-content/uploads/2020/05/26-java-logo.jpg
---
No <a title="Exceções do Java são úteis: talvez você é que não saiba usá-las" href="https://devkico.itexto.com.br/?p=2211">meu último post</a>&nbsp;falei um pouco sobre um dos meus recursos favoritos do Java que são as <em>exceptions</em>. Lá falei um pouco sobre como as exceções nos ajudam a pensar <em>contratos</em>, mas agora gostaria de ir um pouco além. Agora vou mostrar como tornar suas APIs muito mais ricas usando exceptions.
<h2>Mas o que é uma API mesmo?</h2>
Não vou dourar a pílula,&nbsp;sendo assim vou definir de uma forma bem direta:
<blockquote>é a parte executável do seu programa que você irá expor a outros programadores para que eles a&nbsp;executem</blockquote>
Repare que não usei a palavra "sistema" aqui, mas sim "programa". Quando você&nbsp;projeta uma classe que possuirá métodos públicos, pensados para que outros programadores a usem, você está criando uma API (mesmo que você seja este outro programador).

<em>Existe algum programa que não crie uma API Kico?</em> Yeap: pense nos scripts que você escreve para automatizar suas tarefas. É aquele tipo de programa que você apenas quer que seja executado. Softwares feitos <strong>apenas para uso direto</strong> normalmente não expõem uma API.

<em>Ah, mas aí, se outro programa o chamar, eu posso dizer que aquilo ali é uma API?</em> Aí você relativizou a coisa e este post não sai. :)

Falaremos aqui não de APIs REST, mas sim aquela que se manifesta sob a forma de código executável, ou seja, os métodos públicos que você declara em suas classes ou os abstratos em suas interfaces.
<h2>O que é uma boa API?</h2>
Uma boa API é aquela que nos diz exatamente o que será feito. Tudo começa a partir do nome escolhido pelo programador&nbsp;para aquele método, que deverá expor de forma evidente a <em>intenção</em> daquele código.

Um bom nome já nos deu boa parte&nbsp;do que precisamos, o segundo componente são os parâmetros que a API espera. Idealmente devem ser poucos e com uma granularidade adequada. Vamos começar com um exemplo simples que iremos ir melhorando durante o post (sim, é código real):

```java
int cadastrarPessoa(int id, String nome, String sobrenome, Date dataNascimento)
```

Reparou como a granularidade está errada? Por que não simplesmente passar um objeto do tipo Pessoa a ser persistido,&nbsp;tal como a versão melhorada que mostrarei a seguir? Ainda não é uma API perfeita, mas é inegavelmente mais simples (e o programador sofre menos no momento em que for digitar seu código):

```java
class Pessoa {
int cadastrarPessoa(Pessoa pessoa) (...)
}
```

Há outro aspecto na API que deve ser levado em consideração: o tipo de retorno. Nossa versão anterior retorna um valor inteiro, que representa o identificador do registro no banco de dados. Se houver um erro, ela poderia simplesmente nos retornar um valor negativo: "-1: sem id, -2: sem nome, -3: sem sobrenome" e por aí vai. O valor de retorno terá então duplo sentido: o óbvio (retornar o identificador) e identificar um erro (quebra de contrato).

Um cliente da API então escreveria código similar ao exposto a seguir para lidar com erros:

```java
switch (cadastrarPessoa(pessoa) {
case -1:
System.out.println("Opa! Sem o ID que deve ser preenchido antes!");
break;
case -2:
(...)
}
```

Sofrimento eterno que poderia ser um pouco aliviado&nbsp;incluindo algumas constantes, mas ainda seria um sofrimento eterno.&nbsp;Vimos alguns bons pontos na definição de uma API:
<ul>
 	<li>Um bom nome</li>
 	<li>Uma boa definição de parâmetros</li>
 	<li>Um valor de retorno que seja significativo (e possua uma única função)</li>
</ul>
Falta algo: os limites da API, ou seja, <strong>as condições para que ela funcione</strong>. É muito difícil tornar isto explícito em uma API REST, mas com código executável, especialmente em linguagens que possuam um recurso como as exceptions do Java, não. Como seria uma terceira versão da nossa API?

```java
class PessoaNegocio {
void cadastrar(Pessoa pessoa) throws Validação&nbsp;(...)
}
```

Não preciso mais retornar um valor inteiro: se a persistência for bem sucedida, o próprio método já vai preencher o atributo "id" do objeto que passei como parâmetro. E se algo der errado? Uma exceção chamada&nbsp;<em>Validação</em> (evite caracteres especiais no seu código) será disparada, e&nbsp;<strong>nesta</strong> se encontrarão os detalhes a respeito do que deu errado.

A exceção é parte do contrato: ela nos diz algo como:
<blockquote>Ok, vou cadastrar esta pessoa no banco de dados, mas apenas se o objeto tiver valores válidos para todos os atributos.</blockquote>
Nossa API agora tem um limite bem definido: você lê a assinatura do método e sabe que somente objetos válidos, ou seja, aqueles cujo estado interno esteja de acordo com o que se espera, será persistido no banco de dados.

O programador agora pode escrever código ainda mais interessante:

```java
try {
// o fluxo principal fica BEM isolado
negocio.cadastrar(pessoa);
} catch (Validação erroValidacao) {
// eu sei que meu problema é de validação
// talvez eu possa projetar algum comportamento
// de retentativa, ou mesmo informar melhor o
// usuário final a respeito da bobagem que está
// tentando fazer
}
```

E aqui entra mais um ponto que você deve levar em consideração quando for escrever sua API: os erros que talvez&nbsp;seus clientes não consigam aliviar. Uma falha no seu SGBD. Será que seria legal tentar melhorar um pouco mais nossa API tal como no exemplo abaixo?

```java
void cadastrar(Pessoa pessoa) throws Validação, JDBCException
```

Antes eu sabia que objetos inválidos não seriam persistidos: agora também sei que uma falha no banco de dados pode ocorrer. Mais do que isto, sei que é um banco de dados relacional (JDBCException). Aqui entra o seu contexto.

Você quer que os usuários da sua API&nbsp;<strong>saibam</strong> que por trás dos panos está um SGBD relacional? Se sim, ok. Se não, trate internamente estes problemas e dispare uma exceção do tipo&nbsp;<em>RuntimeException</em> ou derivadas. Você estará aqui expondo detalhes de uma camada inferior sem necessidade alguma, e ainda tornando mais difícil a vida dos usuários da sua API.

Agora, se você quer expor este aspecto do sistema, perfeito: há aqui uma delegação de responsabilidade. O cliente da sua API terá de lidar explicitamente com erros provenientes da camada inferior do sistema.
<h2>Uma rápida menção ao Groovy</h2>
<img class="aligncenter size-full wp-image-235" src="https://devkico.itexto.com.br/wp-content/uploads/2008/12/groovylogo.png" alt="groovylogo" width="203" height="100">

Groovy é uma linguagem que visa desburocratizar o trabalho do desenvolvedor. Uma das formas que faz isto é através do modo como lidamos com exceções do tipo checked.

Enquanto no Java, código que irá chamar um método que dispara uma exceção obrigatoriamente deve envolver a chamada ao método em um bloco catch ou incluir a declaração da exceção no método que o chamará, em Groovy este não é o caso. Então, código Java similar ao exposto abaixo:

```java
try {
negocio.cadastrar(pessoa);
} catch (Validação ex) {
// trato aqui
}
```

ou

```java
void executaAlgo() throws Validação {
(...)
negocio.cadastra(pessoa);
(...)
}
```

Em Groovy eu apenas chamo o método e o incluo em um bloco try... catch ou adiciono&nbsp;uma declaração do tipo throws &nbsp;se eu quiser. Ok, ignoro então as exceptions? Não.

Se você vai projetar&nbsp;uma API, tudo o que disse em relação ao Java também se aplica ao Groovy, pois exceptions nos ajudam a explicitar os limites da mesma.
<h2>Concluindo</h2>
Meu objetivo neste post foi ir além do uso padrão das exceptions como uma ferramenta que nos possibilita escrever código mais robusto. Como puderam ver, elas também nos ajudam a implementar&nbsp;<strong>melhores</strong> APIs: com contrato melhor explicitado, mais fáceis de usar e que, consequentemente, acabarão por criar sistemas também mais robustos.