Esse blog traz várias dicas de programação, principalmente voltadas para o mundo Java, mas também fala sobre Front-End, Banco de Dados e muito mais.
quinta-feira, 18 de dezembro de 2025
API first - ótimo com IA
segunda-feira, 20 de outubro de 2025
Idempotência - A necessidade de garantir integridade em chamadas distribuídas
Idempotência significa que executar a mesma operação várias vezes tem o mesmo efeito que executá-la uma única vez. Para escrever um CRUD que não permita entradas duplicadas por falha de rede, reenvio automático, duplicidade, etc é preciso atenção na implementação e usar alguns padrões.
Podemos usar Chaves Naturais: Quando um recurso já possui um identificador natural único (como CPF, CNPJ, número de contrato, e-mail, matrícula etc.), podemos utilizá-lo como chave principal no cadastro. Isso elimina a possibilidade de duplicidade no nível lógico do sistema.
Controle de Duplicidade no Banco de Dados: está bem ligada com a primeira opção, já que quando usamos chaves naturais costumamos controlar via banco como PK ou pelo menos unique constraint. Mas aqui também pode estrapolar esse conceito e usar outros campos em constraints, criando padrões que não podem se repetir. Dependendo do caso uma data ou outro campo podem ser usados para criar essa verificação no Banco de Dados, e ao tentar inserir os dois registros irá barrar.
Outra opção usada é a “Idempotency Key” (chave única por requisição). O cliente gera uma chave única (ex: UUID) e a envia no cabeçalho da requisição, então o servidor armazena o resultado da primeira requisição associada a essa chave e se a mesma chave for usada novamente, o servidor retorna o mesmo resultado, sem processar a operação novamente.
Onde essa chave é armazenada? No Banco de Dados ou Cache Distribuído (Redis, Memcached, etc.). Dependendo da aplicação, pode-se guardar nos 2 locais. Uma terceira opção seria a sessão do usuário, para aplicações web autenticadas que guardam sessão.
Para o caso de clique nervoso do usuário, desabilitar o botão no momento do clique até que volte uma resposta também pode ajudar. Isso também vai depender da situação, do sistema, etc., mas sim, para alguns casos, desabilitar o botão enquanto o servidor responde pode resolver muita coisa.
E aí, tem mais alguma estratégia para garantir a idempotência? Deixa aí nos comentários...
quinta-feira, 12 de agosto de 2021
Consumindo API Rest com Spring Boot usando a classe WebClient
Nesse artigo vou mostrar como fazer um cliente para uma API Rest com Spring 5 usando a classe WebClient que é parte do novo WebFlux Framework. Esse cliente vai consumir uma API de Livros. Aproveitando o post, já vou criar esse cliente em uma classe de teste usando JUnit para mostrar o teste de APIs com ele. Mas a forma de uso aqui não se limita a testes.
Como o foco é a construção de um cliente para consumir uma API, vou omitir o código da API, trazendo só a construção do cliente. Então vamos ao código:
import java.util.List;
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import
org.springframework.web.reactive.function.client.WebClient.Builder;
import
org.springframework.web.reactive.function.client.WebClient.ResponseSpec;
import br.com.socialbooks.model.Livro;
import reactor.core.publisher.Mono;
class LivroControllerTest {
@BeforeAll
static void inserirLivro() {
// configura uma requisição
Builder
builder = WebClient.builder();
builder.baseUrl("http://localhost:8080");
Livro
livro = new Livro("Aprendendo Spring boot");
// prepara o cliente
WebClient
client = builder.build();
ResponseSpec
response = client.method(HttpMethod.POST).uri("/livros").bodyValue(livro).retrieve();
// recebendo o recurso salvo
Mono<Livro>
monoLivro = response.bodyToMono(Livro.class);
livro = monoLivro.block();
}
@Test
void testGetListaLivros() {
// configura uma requisição
Builder
builder = WebClient.builder();
builder.baseUrl("http://localhost:8080");
builder.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
// prepara o client
WebClient
client = builder.build();
ResponseSpec
response = client.method(HttpMethod.GET).uri("/livros").retrieve();
/*
* recebe o Mono que executa
de forma assíncrona. OBS: Para carregar uma
lista
* de objetos, é preciso usar
o ParameterizedTypeReference.
*/
Mono<List<Livro>>
monoLivro = response.bodyToMono(new
ParameterizedTypeReference<List<Livro>>() {
});
// como funciona de forma assincrona, usamos o block para forçar ele esperar a resposta.
List<Livro>
livros = monoLivro.block();
Assert.assertTrue(livros.get(0).getTituloLivro().equals("Aprendendo Spring boot"));
}
@Test
void testGetLivro() {
// configura uma requisição
Builder
builder = WebClient.builder();
builder.baseUrl("http://localhost:8080");
builder.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
// prepara o client
WebClient
client = builder.build();
ResponseSpec response = client.method(HttpMethod.GET).uri("/livros/1").retrieve();
// recebe o Mono que executa de forma
assíncrona
Mono<Livro>
monoLivro = response.bodyToMono(Livro.class);
// como funciona de forma assincrona, usamos o block para forçar ele esperar
// a resposta.
Livro livro = monoLivro.block();
Assert.assertTrue(livro.getTituloLivro().equals("Aprendendo Spring boot"));
}
}
Pra esse artigo é só.