REST Countries API
API intermediária em Java 21/Spring Boot 3 para consumo da REST Countries API pública, com foco em práticas de SRE e DevOps — observabilidade, resiliência, infraestrutura como código e evolução incremental por releases.
dezembro de 2024
Contexto
O Journey Lab é uma iniciativa pessoal de evolução técnica com foco na migração para áreas de SRE e Cloud/DevOps. O REST Countries API é o primeiro projeto dessa jornada, simulando um cenário real de integração com serviço externo para aplicar boas práticas de observabilidade, resiliência, infraestrutura como código, automação e escalabilidade.
O projeto foi desenvolvido de forma incremental, seguindo um modelo de evolução por releases — semelhante ao fluxo de times de engenharia maduros — com cada versão adicionando uma camada de qualidade e maturidade operacional ao sistema.
Problema
Consumir APIs externas em sistemas reais apresenta desafios que projetos de estudo convencionais não simulam:
- Indisponibilidade: a API externa pode estar fora do ar ou com latência alta
- Testes frágeis: testar contra a API real torna os testes não determinísticos e lentos
- Acoplamento forte: sem abstração, o sistema inteiro é afetado por mudanças no contrato externo
- Observabilidade ausente: sem logs e métricas, falhas de integração são invisíveis em produção
A ausência de estratégias para lidar com esses problemas é o que diferencia um projeto de estudo de um sistema pronto para produção.
Solução
Construção de uma API intermediária que consome a REST Countries API pública, aplicando camadas de abstração e qualidade sobre a integração:
- Camada de cliente isolada via OpenFeign, com mapeamento de erros externos para exceções internas
- Testes de integração com WireMock para simular a API externa sem depender dela
- Ambiente de testes isolado e reprodutível com Testcontainers
- Evolução por releases, cada uma com foco em um aspecto de qualidade específico
Arquitetura
Cliente HTTP
│
▼
CountryController (REST)
│ GET /countries/{name}
▼
CountryServiceImpl (implementa CountryServicePort)
│ aplica regras e transforma a resposta
▼
CountryClient (OpenFeign)
│ GET /name/{name}
▼
REST Countries API (externa) ou WireMock (local/testes)
A separação via interface CountryServicePort isola o contrato da implementação, facilitando testes e substituição de dependências. O CountryClient via OpenFeign é a única fronteira com o mundo externo — em desenvolvimento e CI, o WireMock assume esse papel via Docker Compose.
API
| Método | Endpoint | Resposta | Descrição |
|---|---|---|---|
GET | /countries/{name} | 200 | Retorna informações do país pelo nome |
Resposta de sucesso (200 OK):
{
"name": "Brazil",
"capital": "Brasilia",
"region": "Americas",
"population": 212559409,
"flagUrl": "https://flagcdn.com/w320/br.png"
}
A URL da API externa é configurável via variável de ambiente COUNTRY_API_URL, permitindo alternar entre o WireMock local e a API real (https://restcountries.com/v3.1) sem alterar o código.
Stack Utilizada
| Camada | Tecnologia |
|---|---|
| Backend | Java 21, Spring Boot 3.4.8, Spring Cloud 2024.0.2 |
| Integração | Spring Cloud OpenFeign (cliente HTTP declarativo) |
| Infraestrutura local | Docker + Docker Compose, WireMock (container standalone) |
| Utilitários | Lombok (redução de boilerplate), Spring Boot Actuator (health/métricas) |
| Testes | JUnit 5, Spring Boot Test |
Decisões Técnicas
WireMock via Docker Compose (não embarcado nos testes): no v0.1.0, o WireMock roda como um container standalone gerenciado pelo Docker Compose, na porta 10104. A URL é injetada na aplicação via variável de ambiente COUNTRY_API_URL=http://wiremock:8080/v3.1. Isso permite simular a API externa localmente sem alterar código — basta trocar a variável para apontar para a API real. A integração com Testcontainers é planejada para releases futuras.
Interface CountryServicePort: o contrato do serviço é definido em uma interface, desacoplando Controller de implementação. Isso facilita testes unitários com mocks e permite evoluir a implementação sem impactar quem depende do contrato.
Java Records para DTOs: CountryResponse e os DTOs aninhados de CountryApiResponse (incluindo CountryName e CountryFlags) são Java records — imutáveis por padrão, com equals, hashCode e toString gerados automaticamente.
Evolução por releases com intenção clara: em vez de tentar construir o sistema "completo" de uma vez, cada release adiciona uma camada de maturidade com objetivo específico. Isso simula como times de engenharia maduros trabalham e permite aprender de forma estruturada.
Desafios
Configuração do WireMock com cenários dinâmicos: a documentação do WireMock é extensa, mas configurar cenários que simulam timeout real (não apenas delay), conexão encerrada abruptamente e respostas malformadas exigiu experimentação.
Integração Testcontainers + WireMock: fazer o WireMock rodar dentro de um container gerenciado pelo Testcontainers, com a URL dinâmica sendo injetada no Feign Client em runtime, exigiu entender o ciclo de vida dos containers durante os testes.
Tratamento de exceções do Feign: o Feign lança exceções genéricas por padrão. Implementar um ErrorDecoder customizado para converter cada tipo de erro externo em uma exceção de domínio específica foi mais trabalhoso do que o esperado, especialmente para casos como RetryableException (timeout) vs FeignException (erros HTTP).
Aprendizados
Testes de integração são parte do design, não validação posterior. Escrever os testes de integração antes ou junto com o código forçou decisões melhores de arquitetura — especialmente na separação do Feign Client como uma fronteira isolável.
WireMock muda a forma de pensar sobre contratos externos. Ao definir os stubs do WireMock, você é forçado a documentar explicitamente o que a API externa pode retornar (incluindo cenários de erro), o que melhora o entendimento do sistema como um todo.
Observabilidade deve ser planejada desde o início. Adicionar logs estruturados retroativamente é muito mais trabalhoso do que incluí-los na implementação inicial. Esse foi o principal aprendizado que vai guiar as próximas releases do projeto.
Roadmap de Releases
| Release | Objetivo | Status |
|---|---|---|
| 0.1 | API funcional com consumo da REST Countries | ✅ Concluída |
| 0.2 | Cache com Caffeine | ⏳ Planejada |
| 0.3 | Observabilidade com Micrometer + Prometheus | ⏳ Planejada |
| 0.4 | Testes de integração com Testcontainers | ⏳ Planejada |
| 0.5 | Dockerização completa | ⏳ Planejada |
| 0.6 | Deploy com Terraform em AWS ECS | ⏳ Planejada |
| 0.7 | API Gateway + autenticação básica | ⏳ Planejada |
| 0.8 | Alertas e dashboards com Grafana/Datadog | ⏳ Planejada |
| 0.9 | Resiliência: Retry, Timeout, Circuit Breaker | ⏳ Planejada |
| 1.0 | Publicação no GitHub + LinkedIn | ⏳ Planejada |