API de simulação de processamento de transações bancárias (PIX, TED e TEF) desenvolvida com Java 21 e Spring Boot 4.0.6. A aplicação segue os princípios de Arquitetura Hexagonal e Domain-Driven Design, com suporte a autenticação via OAuth2/JWT, controle de rate limiting, auditoria regulatória, validação de saldo e limites transacionais, criptografia em repouso, integridade por HMAC, mensageria com Outbox Pattern + Kafka, métricas Prometheus/Micrometer e tracing OpenTelemetry.
Estado da documentação: junho/2026.
| Tecnologia | Versão | Finalidade |
|---|---|---|
| Java | 21 | Linguagem principal |
| Spring Boot | 4.0.6 | Framework de aplicação |
| Spring Security | (gerenciado pelo Boot) | Autenticação e autorização |
| Spring Data JPA / Hibernate | (gerenciado pelo Boot) | Persistência |
| Spring for Apache Kafka | (gerenciado pelo Boot) | Publicação e consumo de eventos |
| Spring Boot Actuator | (gerenciado pelo Boot) | Health checks e informações operacionais |
| Micrometer + Prometheus | (gerenciado pelo Boot) | Métricas customizadas e endpoint Prometheus |
| OpenTelemetry | (gerenciado pelo Boot) | Rastreamento distribuído via OTLP |
| PostgreSQL | 42.7.11 | Banco de dados relacional |
| Flyway | 12.6.2 | Versionamento de schema |
| Bucket4j | 8.19.0 | Rate limiting por token bucket |
| SpringDoc OpenAPI | 3.0.3 | Documentação interativa (Swagger UI) |
| Jackson 3 | (gerenciado pelo Boot) | Serialização JSON |
| Logstash Logback Encoder | 9.0 | Logs estruturados em JSON (Logstash) |
| JaCoCo | 0.8.15 | Relatórios e gates de cobertura de testes |
| Testcontainers | (gerenciado pelo Boot) | Testes de integração com containers |
| ArchUnit | 1.4.2 | Testes de arquitetura |
| H2 | (gerenciado pelo Boot) | Banco em memória para testes |
| Módulo | Responsabilidade |
|---|---|
transacao |
Processamento de PIX, TED e TEF — domínio central, casos de uso, controller, DTOs, eventos e strategies |
conta |
Domínio de conta, saldo e limite transacional |
auditoria |
Registro de eventos de auditoria regulatória |
compartilhado |
Utilitários, filtros, exceções, criptografia, HMAC, idempotência e constantes transversais |
configuracao |
Configurações Spring: segurança, Kafka, persistência, beans e filtros |
infraestrutura |
Adaptadores JPA, entidades e repositórios Spring Data |
integracao |
Adaptadores para integrações externas (antifraude stub implementado; Bacen/STR/SPB ainda planejados) |
mensageria |
Outbox Pattern, publicação Kafka, consumo, roteamento de eventos e monitoramento de DLQ |
observabilidade |
Mascaramento de dados sensíveis, logging estruturado em JSON, métricas Micrometer e tracing OpenTelemetry |
- Domínio imutável:
Transacao,Saldo,LimiteTransacionaleContaimplementados com Builder Pattern — nenhuma mutação direta de estado. - Value Object:
ValorMonetariocomorecordcom validação inline e fábrica estáticaparaReal(). - Strategy Pattern:
StrategyResolvercom lista injetável deTransacaoStrategy— extensível sem alteração do orquestrador. - Porta de repositório:
TransacaoRepository,ContaRepository,SaldoRepositoryeLimiteRepositorycomo interfaces no módulo de aplicação — infraestrutura desacoplada. - Resposta padronizada:
GlobalExceptionHandlercomProblemDetail(RFC 7807). - Correlação ponta-a-ponta:
ContextoRequisicaoFiltercomHIGHEST_PRECEDENCEpropagandoidCorrelacaovia MDC (Mapped Diagnostic Context). - Criptografia em repouso:
CriptografiaConvertercomoAttributeConverterJPA usando AES-256-GCM com IV aleatório por valor — campos sensíveis das entidades armazenados criptografados no banco. - Integridade com HMAC:
HmacServicecom HMAC-SHA256 para geração de assinaturas determinísticas de dados sensíveis pesquisáveis, configurável viaHmacProperties. - Mascaramento de logs: subsistema
observabilidade/mascaramentocom estratégias intercambiáveis (MascaraStrategy) para JSON, headers, mensagens e stack traces — integrado ao Logback viaLogMascaramentoConvertereJsonMascaradoProvider. - Eventos de domínio + Outbox: eventos (
TransacaoIniciadaEvento,TransacaoConcluidaEvento,TransacaoFalhouEvento,TransacaoEstornadaEvento) persistidos na tabela de outbox e publicados no Kafka por job agendado. - Consumo resiliente: consumidores Kafka com tratamento de erro via
DefaultErrorHandler+DeadLetterPublishingRecoverer, encaminhamento para*.DLQ, idempotência de consumo porevento_processadoe roteamento detransacoes.iniciadaspara processadores específicos de PIX, TED e TEF. - Observabilidade: métricas customizadas de transações, saldo, limite, idempotência e outbox via Micrometer/Prometheus; tracing OpenTelemetry configurado por perfil.
- Cobertura de testes: 58 arquivos de teste cobrindo domínio, services, strategies, controller, persistência, mensageria, filtros, criptografia/HMAC, arquitetura, integrações com Testcontainers, métricas e mascaramento de logs; JaCoCo configurado com relatórios HTML/XML/CSV e gates de cobertura por bundle, domínio e aplicação.
- Camada de controle HTTP (
TransacaoController) separada da camada de aplicação. - Camada de aplicação (
ProcessaTransacaoService,CriaTransacaoService,ConsultaTransacaoService,EstornoTransacaoService) orquestrando casos de uso sem dependência direta de infraestrutura. - Camada de domínio (
Transacao,Conta,Saldo,LimiteTransacional,ValorMonetario) sem dependência de frameworks. - Camada de infraestrutura (
TransacaoJpaAdapter,ContaJpaAdapter,SaldoJpaAdapter,LimiteJpaAdapter,AuditoriaJpaAdapter,OutboxEventoJpaAdapter,EventoProcessadoJpaAdapter) implementando interfaces de porta. - Validações específicas por tipo de transação via strategies: PIX simula envio ao SPI/BACEN, TED valida dia útil e horário bancário, TEF consulta antifraude stub.
- Filtros transversais (
ContextoRequisicaoFilter,RateLimitFilter) na camada de entrada. - Criptografia em repouso via
CriptografiaConverter(AES-256-GCM,AttributeConverterJPA). - Mascaramento de dados sensíveis nos logs via subsistema
observabilidade/mascaramentointegrado ao Logback com logging estruturado em JSON (logstash-logback-encoder). - HMAC-SHA256 para geração de assinaturas determinísticas de dados sensíveis (
HmacService,HmacUtils). - Eventos de domínio e outbox (
DominioEventoOutboxPublicador,OutboxEvento,OutboxEventoRepository,OutboxEventoJpaAdapter,EventoOutboxPublicador) com publicação Kafka assíncrona. - Consumidores Kafka (
TransacaoIniciadaKafkaConsumidor,PixTransacaoKafkaConsumidor,TedTransacaoKafkaConsumidor,TefTransacaoKafkaConsumidor,DlqMonitorConsumidor) com idempotência porEventoProcessadoService, roteamento por tipo de transação e publicação de eventos finais. - Configuração de Kafka + DLQ + tópicos (
KafkaConfig,KafkaDlqProperties,OutboxProperties,TopicosProperties) condicionada porapp.eventos.kafka.enabled. - Métricas customizadas via
TransacaoMetricaseOutboxMetricas, com exportação Prometheus pelo Actuator. - Tracing distribuído via Micrometer Tracing/OpenTelemetry com exportação OTLP configurável por perfil.
- Integrações externas: portas definidas (
AntiFraudeGateway,PixGateway), adaptador de antifraude implementado como stub funcional com threshold configurável; integrações reais com SPI/BACEN, STR, SPB e DICT ainda sem implementação de produção. - Auditoria: captura automática via
AuditoriaListener(JPA lifecycle callbacks) operacional; mascaramento de dados sensíveis nos eventos de auditoria não implementado. - Bounded contexts separados para
cliente,PIX,TEDeTEFjá possuem estrutura inicial de pacotes, mas ainda sem implementação funcional própria; o domínio e os casos de uso permanecem concentrados emtransacao. - Observabilidade: métricas e tracing estão instrumentados/configurados na aplicação; dashboards Grafana, alertas de SLO e coletores externos permanecem fora do repositório.
- Consumidores Kafka por tipo estão implementados, mas ainda operam sobre o domínio central de
transacao; a separação funcional completa por bounded context segue planejada. - Camada de resiliência (circuit breaker, retry, bulkhead por integração).
- Domínio de
ClienteeChavePix. - Integração real com SPI/BACEN, STR e DICT.
transaction-processing-api/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/com/mekylei/transactionprocessing/
│ │ │ ├── TransactionProcessingApiApplication.java
│ │ │ ├── auditoria/
│ │ │ │ ├── AuditoriaListener.java
│ │ │ │ ├── AuditoriaService.java
│ │ │ │ ├── DadosAuditoria.java
│ │ │ │ ├── aplicacao/
│ │ │ │ │ └── AuditoriaContextPadrao.java
│ │ │ │ ├── dominio/
│ │ │ │ │ ├── AcaoAuditoria.java
│ │ │ │ │ └── AuditoriaEvento.java
│ │ │ │ └── porta/
│ │ │ │ ├── AuditoriaContextGateway.java
│ │ │ │ ├── AuditoriaContextWriter.java
│ │ │ │ └── AuditoriaRepository.java
│ │ │ ├── cliente/ (estrutura inicial — planejado)
│ │ │ │ ├── aplicacao/
│ │ │ │ │ └── porta/
│ │ │ │ │ └── repositorio/
│ │ │ │ └── dominio/
│ │ │ ├── compartilhado/
│ │ │ │ ├── adaptador/
│ │ │ │ │ └── Jackson3FormatMapper.java
│ │ │ │ ├── constantes/
│ │ │ │ │ ├── HeadersHttp.java
│ │ │ │ │ └── ProblemaDetailConstantes.java
│ │ │ │ ├── dominio/
│ │ │ │ │ ├── TipoTransacao.java
│ │ │ │ │ └── ValorMonetario.java
│ │ │ │ ├── evento/
│ │ │ │ │ └── EventoDominio.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── ApiException.java
│ │ │ │ │ ├── BaseException.java
│ │ │ │ │ ├── GlobalExceptionHandler.java
│ │ │ │ │ ├── KafkaPublicarException.java
│ │ │ │ │ ├── RecursoNaoEncontradoException.java
│ │ │ │ │ ├── RegraNegocioException.java
│ │ │ │ │ └── SaldoInsuficienteException.java
│ │ │ │ ├── idempotencia/
│ │ │ │ │ └── IdempotenciaService.java
│ │ │ │ ├── seguranca/
│ │ │ │ │ ├── HmacService.java
│ │ │ │ │ ├── HmacUtils.java
│ │ │ │ │ └── RoleTransacao.java
│ │ │ │ └── util/
│ │ │ │ ├── CalendarioStubBacenService.java
│ │ │ │ ├── CorrelacaoUtil.java
│ │ │ │ ├── CriptografiaConverter.java
│ │ │ │ └── DateTimeUtil.java
│ │ │ ├── configuracao/
│ │ │ │ ├── docs/ (vazio — planejado)
│ │ │ │ ├── kafka/
│ │ │ │ │ ├── KafkaConfig.java
│ │ │ │ │ ├── KafkaDlqProperties.java
│ │ │ │ │ ├── OutboxProperties.java
│ │ │ │ │ └── TopicosProperties.java
│ │ │ │ ├── persistencia/
│ │ │ │ │ └── HmacProperties.java
│ │ │ │ ├── resiliencia/ (vazio — planejado)
│ │ │ │ ├── seguranca/
│ │ │ │ │ ├── ApiAcessoNegadoHandler.java
│ │ │ │ │ ├── ApiAutenticacaoEntryPoint.java
│ │ │ │ │ ├── JwtClaimsConverter.java
│ │ │ │ │ ├── RateLimitResposta.java
│ │ │ │ │ └── SecurityConfig.java
│ │ │ │ └── spring/
│ │ │ │ ├── bean/
│ │ │ │ │ ├── AppConfig.java
│ │ │ │ │ ├── HibernateJsonConfig.java
│ │ │ │ │ └── StrategyConfig.java
│ │ │ │ └── filter/
│ │ │ │ ├── ContextoRequisicaoFilter.java
│ │ │ │ └── RateLimitFilter.java
│ │ │ ├── conta/
│ │ │ │ ├── aplicacao/
│ │ │ │ │ ├── porta/
│ │ │ │ │ │ └── repositorio/
│ │ │ │ │ │ ├── ContaRepository.java
│ │ │ │ │ │ ├── LimiteRepository.java
│ │ │ │ │ │ └── SaldoRepository.java
│ │ │ │ │ └── servico/
│ │ │ │ │ ├── LimiteService.java
│ │ │ │ │ └── SaldoService.java
│ │ │ │ ├── controle/ (vazio — planejado)
│ │ │ │ └── dominio/
│ │ │ │ ├── Conta.java
│ │ │ │ ├── LimiteTransacional.java
│ │ │ │ ├── Saldo.java
│ │ │ │ ├── StatusConta.java
│ │ │ │ └── TipoConta.java
│ │ │ ├── infraestrutura/
│ │ │ │ ├── entidade/
│ │ │ │ │ ├── AuditoriaEventoEntity.java
│ │ │ │ │ ├── ContaEntity.java
│ │ │ │ │ ├── EventoProcessadoEntity.java
│ │ │ │ │ ├── LimiteTransacionalEntity.java
│ │ │ │ │ ├── OutboxEventoEntity.java
│ │ │ │ │ ├── SaldoEntity.java
│ │ │ │ │ ├── StatusOutboxEvento.java
│ │ │ │ │ └── TransacaoEntity.java
│ │ │ │ ├── persistencia/
│ │ │ │ │ ├── AuditoriaJpaAdapter.java
│ │ │ │ │ ├── ContaJpaAdapter.java
│ │ │ │ │ ├── EventoProcessadoJpaAdapter.java
│ │ │ │ │ ├── LimiteJpaAdapter.java
│ │ │ │ │ ├── OutboxEventoJpaAdapter.java
│ │ │ │ │ ├── SaldoJpaAdapter.java
│ │ │ │ │ └── TransacaoJpaAdapter.java
│ │ │ │ └── repositorio/
│ │ │ │ ├── AuditoriaJpaRepository.java
│ │ │ │ ├── ContaJpaRepository.java
│ │ │ │ ├── EventoProcessadoJpaRepository.java
│ │ │ │ ├── LimiteJpaRepository.java
│ │ │ │ ├── OutboxEventoJpaRepository.java
│ │ │ │ ├── SaldoJpaRepository.java
│ │ │ │ └── TransacaoJpaRepository.java
│ │ │ ├── integracao/
│ │ │ │ ├── antifraude/
│ │ │ │ │ └── AntiFraudeStubAdapter.java
│ │ │ │ ├── bacen/ (vazio — planejado)
│ │ │ │ ├── spb/ (vazio — planejado)
│ │ │ │ └── str/ (vazio — planejado)
│ │ │ ├── mensageria/
│ │ │ │ ├── consumidor/
│ │ │ │ │ ├── DlqMonitorConsumidor.java
│ │ │ │ │ ├── PixTransacaoKafkaConsumidor.java
│ │ │ │ │ ├── TedTransacaoKafkaConsumidor.java
│ │ │ │ │ ├── TefTransacaoKafkaConsumidor.java
│ │ │ │ │ └── TransacaoIniciadaKafkaConsumidor.java
│ │ │ │ ├── aplicacao/
│ │ │ │ │ ├── EventoProcessadoService.java
│ │ │ │ │ └── porta/
│ │ │ │ │ └── EventoProcessadoRepository.java
│ │ │ │ ├── evento/
│ │ │ │ │ └── TransacaoEventoRouter.java
│ │ │ │ ├── outbox/
│ │ │ │ │ ├── DominioEventoOutboxPublicador.java
│ │ │ │ │ ├── EventoOutboxPublicador.java
│ │ │ │ │ ├── OutboxEvento.java
│ │ │ │ │ └── OutboxEventoRepository.java
│ │ │ │ └── produtor/
│ │ │ │ └── KafkaEventoProdutor.java
│ │ │ ├── observabilidade/
│ │ │ │ ├── logging/
│ │ │ │ │ ├── LogMascaramentoConverter.java
│ │ │ │ │ ├── JsonMascaradoProvider.java
│ │ │ │ │ └── TipoCampoMascarado.java
│ │ │ │ ├── mascaramento/
│ │ │ │ │ ├── DadosSensiveisMasker.java
│ │ │ │ │ ├── MascaraPadrao.java
│ │ │ │ │ └── estrategia/
│ │ │ │ │ ├── AbstractRegexMascaraStrategy.java
│ │ │ │ │ ├── HeaderMascaradoStrategy.java
│ │ │ │ │ ├── JsonMascaradoStrategy.java
│ │ │ │ │ ├── MascaraStrategy.java
│ │ │ │ │ ├── MensagemMascaradaStrategy.java
│ │ │ │ │ ├── StacktraceMascaradoStrategy.java
│ │ │ │ │ └── StrategyMascaramentoResolver.java
│ │ │ │ ├── metrica/
│ │ │ │ │ ├── OutboxMetricas.java
│ │ │ │ │ ├── TransacaoMetricas.java
│ │ │ │ │ ├── TransacaoMetricasNoop.java
│ │ │ │ │ └── TransacaoMetricasPort.java
│ │ │ │ └── rastreamento/
│ │ │ │ └── TracingConfig.java
│ │ │ ├── pix/ (estrutura inicial — planejado)
│ │ │ │ ├── aplicacao/
│ │ │ │ │ ├── porta/
│ │ │ │ │ │ └── integracao/
│ │ │ │ │ └── servico/
│ │ │ │ ├── controle/
│ │ │ │ ├── dominio/
│ │ │ │ ├── dto/
│ │ │ │ ├── enums/
│ │ │ │ ├── mapper/
│ │ │ │ └── validador/
│ │ │ ├── ted/ (estrutura inicial — planejado)
│ │ │ │ ├── aplicacao/
│ │ │ │ │ ├── porta/
│ │ │ │ │ │ └── integracao/
│ │ │ │ │ └── servico/
│ │ │ │ ├── controle/
│ │ │ │ └── dto/
│ │ │ ├── tef/ (estrutura inicial — planejado)
│ │ │ │ ├── aplicacao/
│ │ │ │ │ └── servico/
│ │ │ │ └── controle/
│ │ │ └── transacao/
│ │ │ ├── aplicacao/
│ │ │ │ ├── porta/
│ │ │ │ │ ├── evento/
│ │ │ │ │ │ └── EventoPublicador.java
│ │ │ │ │ ├── integracao/
│ │ │ │ │ │ ├── AntiFraudeGateway.java
│ │ │ │ │ │ └── PixGateway.java
│ │ │ │ │ └── repositorio/
│ │ │ │ │ └── TransacaoRepository.java
│ │ │ │ └── servico/
│ │ │ │ ├── ConsultaTransacaoService.java
│ │ │ │ ├── CriaTransacaoService.java
│ │ │ │ ├── EstornoTransacaoService.java
│ │ │ │ └── ProcessaTransacaoService.java
│ │ │ ├── controle/
│ │ │ │ ├── TransacaoController.java
│ │ │ │ └── dto/
│ │ │ │ ├── EstornoRequisicao.java
│ │ │ │ ├── EstornoResposta.java
│ │ │ │ ├── TransacaoRequisicao.java
│ │ │ │ └── TransacaoResposta.java
│ │ │ ├── dominio/
│ │ │ │ ├── StatusTransacao.java
│ │ │ │ ├── TipoEventoTransacao.java
│ │ │ │ ├── Transacao.java
│ │ │ │ ├── evento/
│ │ │ │ │ ├── TransacaoConcluidaEvento.java
│ │ │ │ │ ├── TransacaoEstornadaEvento.java
│ │ │ │ │ ├── TransacaoFalhouEvento.java
│ │ │ │ │ └── TransacaoIniciadaEvento.java
│ │ │ │ └── vo/
│ │ │ └── estrategia/
│ │ │ ├── PixTransacaoStrategy.java
│ │ │ ├── StrategyResolver.java
│ │ │ ├── TedTransacaoStrategy.java
│ │ │ ├── TefTransacaoStrategy.java
│ │ │ └── TransacaoStrategy.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── application-dev.yml
│ │ ├── application-prod.yml
│ │ ├── logback-spring.xml
│ │ └── db/migration/
│ │ ├── V1__tabelas_iniciais.sql
│ │ ├── V2__auditoria.sql
│ │ ├── V3__mensageria.sql
│ │ └── V4__limite_versao.sql
│ └── test/
│ ├── java/com/mekylei/transactionprocessing/
│ │ ├── arquitetura/
│ │ │ ├── ArquiteturaHexagonalTest.java
│ │ │ ├── CamadaSegurancaTest.java
│ │ │ └── NamingConventionTest.java
│ │ ├── compartilhado/
│ │ │ ├── dominio/
│ │ │ │ └── ValorMonetarioTest.java
│ │ │ ├── idempotencia/
│ │ │ │ └── IdempotenciaServiceTest.java
│ │ │ ├── seguranca/
│ │ │ │ ├── HmacServiceTest.java
│ │ │ │ └── HmacUtilsTest.java
│ │ │ └── util/
│ │ │ ├── CalendarioStubBacenServiceTest.java
│ │ │ ├── CorrelacaoUtilTest.java
│ │ │ ├── CriptografiaConverterIntegrationTest.java
│ │ │ └── CriptografiaConverterTest.java
│ │ ├── configuracao/spring/filter/
│ │ │ ├── ContextoRequisicaoFilterTest.java
│ │ │ └── RateLimitFilterTest.java
│ │ ├── conta/
│ │ │ ├── aplicacao/servico/
│ │ │ │ ├── LimiteServiceTest.java
│ │ │ │ ├── SaldoIntegrationTest.java
│ │ │ │ └── SaldoServiceTest.java
│ │ │ └── dominio/
│ │ │ ├── ContaTest.java
│ │ │ ├── LimiteTransacionalTest.java
│ │ │ └── SaldoTest.java
│ │ ├── infraestrutura/
│ │ │ ├── entidade/
│ │ │ │ └── ContaBancariaEntity.java
│ │ │ ├── persistencia/
│ │ │ │ ├── AuditoriaJpaAdapterTest.java
│ │ │ │ ├── ContaJpaAdapterTest.java
│ │ │ │ ├── EventoProcessadoJpaAdapterTest.java
│ │ │ │ ├── LimiteJpaAdapterTest.java
│ │ │ │ ├── SaldoJpaAdapterTest.java
│ │ │ │ └── TransacaoJpaAdapterTest.java
│ │ │ └── repositorio/
│ │ │ └── ContaBancariaTestRepository.java
│ │ ├── mensageria/
│ │ │ ├── consumidor/
│ │ │ │ ├── DlqMonitorConsumidorTest.java
│ │ │ │ ├── EventoProcessadoServiceTest.java
│ │ │ │ ├── PixTransacaoKafkaConsumidorTest.java
│ │ │ │ ├── TedTransacaoKafkaConsumidorTest.java
│ │ │ │ ├── TefTransacaoKafkaConsumidorTest.java
│ │ │ │ └── TransacaoIniciadaKafkaConsumidorTest.java
│ │ │ ├── evento/
│ │ │ │ └── TransacaoEventoRouterTest.java
│ │ │ ├── outbox/
│ │ │ │ ├── EventoOutboxPublicadorSchedulerTest.java
│ │ │ │ ├── EventoOutboxPublicadorTest.java
│ │ │ │ ├── OutboxEventoIntegrationTest.java
│ │ │ │ └── OutboxEventoJpaAdapterTest.java
│ │ │ └── produtor/
│ │ │ └── KafkaEventoProdutorTest.java
│ │ ├── observabilidade/mascaramento/
│ │ │ ├── DadosSensiveisMaskerTest.java
│ │ │ └── estrategia/
│ │ │ ├── HeaderMascaradoStrategyTest.java
│ │ │ ├── JsonMascaradoStrategyTest.java
│ │ │ ├── MensagemMascaradaStrategyTest.java
│ │ │ └── StrategyMascaramentoResolverTest.java
│ │ ├── observabilidade/metrica/
│ │ │ ├── OutboxMetricasTest.java
│ │ │ └── TransacaoMetricasTest.java
│ │ ├── transacao/
│ │ │ ├── aplicacao/servico/
│ │ │ │ ├── ConsultaTransacaoServiceTest.java
│ │ │ │ ├── CriaTransacaoServiceTest.java
│ │ │ │ ├── EstornoIntegrationTest.java
│ │ │ │ ├── EstornoTransacaoServiceTest.java
│ │ │ │ ├── ProcessaTransacaoIntegrationTest.java
│ │ │ │ └── ProcessaTransacaoServiceTest.java
│ │ │ ├── controle/
│ │ │ │ └── TransacaoControllerTest.java
│ │ │ ├── dominio/
│ │ │ │ ├── TransacaoTest.java
│ │ │ │ └── evento/
│ │ │ │ └── TransacaoEventosTest.java
│ │ │ └── estrategia/
│ │ │ ├── PixTransacaoStrategyTest.java
│ │ │ ├── StrategyResolverTest.java
│ │ │ ├── TedTransacaoStrategyTest.java
│ │ │ └── TefTransacaoStrategyTest.java
│ │ ├── IntegrationTestBase.java
│ │ └── TransactionProcessingApiApplicationTests.java
│ └── resources/
│ ├── application-integration-test.yml
│ └── application-test.yml
┌─────────────────────────────────────────────────────────────────────┐
│ API LAYER │
│ ┌──────────────────────────┐ ┌──────────────────────┐ │
│ │ ContextoRequisicaoFilter │ │ RateLimitFilter │ │
│ └──────────────┬───────────┘ └──────────┬───────────┘ │
│ │ │ │
│ ┌──────▼─────────────────────────▼──────┐ │
│ │ TransacaoController │ │
│ │ /api/v1/transacoes/** │ │
│ └──────────────────────┬────────────────┘ │
└─────────────────────────────────┼───────────────────────────────────┘
│
┌─────────────────────────────────▼───────────────────────────────────┐
│ APPLICATION LAYER │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ ProcessaTransacaoService │ │
│ │ 1. Valida idempotência │ │
│ │ 2. Valida conta de origem │ │
│ │ 3. Valida saldo e limite │ │
│ │ 4. Cria transação PENDENTE e publica evento iniciado │ │
│ │ 5. Resolve a strategy e processa a transação │ │
│ │ 6. Debita saldo e atualiza limite quando COMPLETADA │ │
│ │ 7. Persiste o estado final │ │
│ │ 8. Publica evento final via Outbox │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌────────────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │ ConsultaTransacao │ │ EstornoTransacao │ │ CriaTransacao │ │
│ └────────────────────┘ └──────────────────┘ └────────────────┘ │
│ ┌────────────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │ IdempotenciaService│ │ SaldoService │ │ LimiteService │ │
│ └────────────────────┘ └──────────────────┘ └────────────────┘ │
└─────────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────────────▼───────────────────────────────────┐
│ DOMAIN LAYER │
│ ┌──────────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ Transacao │ │ Conta │ │ ValorMonetario│ │
│ └──────────────┘ └──────────┘ └───────────────┘ │
│ ┌──────────────┐ ┌────────────────────┐ ┌────────────────────┐ │
│ │ Saldo │ │ LimiteTransacional │ │ TransacaoStrategy │ │
│ └──────────────┘ └────────────────────┘ └────────────────────┘ │
└─────────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────────────▼───────────────────────────────────┐
│ INFRASTRUCTURE LAYER │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ TransacaoJpaAdap.│ │ ContaJpaAdapter │ │ SaldoJpaAdapter │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ LimiteJpaAdapter │ │ AuditoriaJpaAdap.│ │ OutboxEventoJPA │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
│ ┌──────────────────┐ │
│ │ AntiFraudeStubAd.│ │
│ └──────────────────┘ │
└─────────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────────────▼───────────────────────────────────┐
│ EXTERNAL INTEGRATIONS │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ PostgreSQL │ │ OAuth2/JWT JWKS │ │ Kafka (SSL) │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
│ ┌──────────────────┐ │
│ │ Antifraude Stub │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Conta
│
├── Saldo
└── LimiteTransacional
Transacao
│
├── idContaOrigem ───────► Conta
└── valor ───────────────► ValorMonetario
AuditoriaEvento
└── referencia recurso e idRecurso auditado
| Role | Permissões |
|---|---|
CLIENTE |
Processar PIX, TED e TEF, e consultar transações |
OPERADOR |
Processar PIX, TED e TEF, e consultar transações |
GERENTE |
Processar PIX, TED e TEF, consultar transações e realizar estorno |
ADMIN |
Processar PIX, TED e TEF, consultar transações e realizar estorno |
SERVICO_INTERNO |
Processar PIX, TED e TEF, e consultar transações |
- OAuth2 Resource Server: configurado em
SecurityConfig.javacom validação de JWT via JWKS endpoint. Suporta Keycloak (dev) e provedores externos (prod) via variáveis de ambienteOAUTH2_JWKS_URIeOAUTH2_ISSUER_URI. - JWT Claims Converter:
JwtClaimsConverterextrai a claimrolesdo token e mapeia paraGrantedAuthorityno formatoROLE_<NOME>. - RBAC (Role-Based Access Control): cinco papéis definidos em
RoleTransacao—CLIENTE,OPERADOR,GERENTE,ADMIN,SERVICO_INTERNO. Os endpoints de PIX, TED, TEF e consulta exigem uma dessas roles; estorno é restrito aGERENTEeADMIN. - Sessão stateless:
SessionCreationPolicy.STATELESSconfigurado. - Rate limiting por IP:
RateLimitFiltervia Bucket4j com capacidade configurável (app.rate-limit.requests-per-minute). Respeita cabeçalhoX-Forwarded-For. - Auditoria regulatória:
AuditoriaListenercaptura eventos JPA lifecycle (@PostPersist,@PostUpdate,@PostLoad) e registra em tabela append-only com regras PostgreSQL impedindo UPDATE e DELETE. - Tratamento de erros de autenticação/autorização:
ApiAutenticacaoEntryPointeApiAcessoNegadoHandlerretornam respostas padronizadas. - Endpoints públicos:
/actuator/health,/actuator/info,/v3/api-docs/**,/swagger-ui/**e/swagger-ui.htmlacessíveis sem autenticação. - Criptografia em repouso:
CriptografiaConverterimplementado comoAttributeConverterJPA com AES-256-GCM e IV aleatório gerado porSecureRandoma cada operação de escrita — campos sensíveis das entidades armazenados criptografados no banco de dados. - Integridade com HMAC:
HmacServicecom HMAC-SHA256 para geração de assinaturas determinísticas, com normalização de valores (trim+toUpperCase) antes do cálculo. Usado para manter campos criptografados pesquisáveis por HMAC, como agência e número de conta. Configurado viaHmacProperties. - Mascaramento de dados sensíveis nos logs: subsistema
observabilidade/mascaramentocom interfaceMascaraStrategye implementações intercambiáveis para JSON fields, headers HTTP, mensagens de log e stack traces. Integrado ao Logback viaLogMascaramentoConvertere ao encoder JSON viaJsonMascaradoProvider(logstash-logback-encoder).
- Java 21 (JDK)
- Maven 3.9+ (opcional, caso não utilize o wrapper
./mvnw) - PostgreSQL 16+
- Kafka 3.9+ (necessário quando
EVENTOS_KAFKA_ENABLED=true) - Authorization Server compatível com OAuth2/OIDC (ex: Keycloak) com JWKS endpoint exposto
| Variável | Descrição | Exemplo |
|---|---|---|
DB_URL |
URL JDBC do banco | jdbc:postgresql://localhost:5432/transacaodb |
DB_USERNAME |
Usuário do banco | ozzy |
DB_PASSWORD |
Senha do banco | ozzy |
OAUTH2_JWKS_URI |
Endpoint JWKS do Authorization Server | https://keycloak.host/realms/bancario/protocol/openid-connect/certs |
OAUTH2_ISSUER_URI |
Issuer URI do Authorization Server | https://keycloak.host/realms/bancario |
APP_CRIPTOGRAFIA_CHAVE |
Chave de criptografia Base64 (AES-256) | MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY= |
APP_HMAC_CHAVE |
Chave usada no HMAC-SHA256 | xK9mP2vL8nQ5rT1wY4zB7cF3hJ6kU8aR0dG2hN5pS7vL9m |
RATE_LIMIT_REQUISICOES_POR_MINUTO |
Limite de requisições por IP por minuto | 60 |
ANTIFRAUDE_LIMITE_APROVACAO_AUTOMATICA |
Valor máximo para aprovação automática no stub | 10000.00 |
EVENTOS_KAFKA_ENABLED |
Habilita publicação/consumo de eventos Kafka | true |
EVENTOS_KAFKA_DLQ_MAX_TENTATIVAS |
Número máximo de tentativas antes da DLQ | 3 |
EVENTOS_KAFKA_DLQ_INTERVALO_REPROCESSAMENTO |
Intervalo de reprocessamento da DLQ | 10s |
EVENTOS_OUTBOX_LOTE_PUBLICACAO |
Tamanho do lote de publicação do outbox | 50 |
EVENTOS_OUTBOX_INTERVALO_PUBLICACAO_MS |
Intervalo do job de publicação do outbox | 5000 |
EVENTOS_OUTBOX_INTERVALO_REPROCESSAMENTO |
Intervalo de reprocessamento do outbox | 30s |
EVENTOS_OUTBOX_TIMEOUT_ENVIO_MS |
Timeout de envio de eventos | 5000 |
KAFKA_BOOTSTRAP_SERVERS |
Endereço do broker Kafka | kafka.lab.home:9094 |
KAFKA_USERNAME |
Usuário SASL/SCRAM do Kafka | app-prod |
KAFKA_PASSWORD |
Senha SASL/SCRAM do Kafka | trocar-esta-senha |
KAFKA_SSL_TRUSTSTORE_LOCATION |
Local do truststore cliente Kafka | file:/caminho/kafka-client-truststore.p12 |
KAFKA_SSL_TRUSTSTORE_PASSWORD |
Senha do truststore cliente Kafka | changeit |
KAFKA_SSL_TRUSTSTORE_TYPE |
Tipo do truststore | PKCS12 |
OTLP_TRACING_ENDPOINT |
Endpoint OTLP para exportação de traces | https://otlp-jaeger.lab.home/v1/traces |
Com o perfil dev, as variáveis de banco, criptografia, HMAC e OAuth2 possuem valores padrão, mas são normalmente sobrescritas via variáveis de ambiente na IDE, por exemplo, ou utilizando um arquivo .env para build com docker compose.
Rodo o Keycloak em um servidor homelab e utilizo um .env para definir os dados de acesso ao banco dele. Este servidor possui uma unidade certificadora que, junto com o gateway (Traefik), possibilita a utilização de HTTPS entre os serviços na rede local. Dessa forma, a chave de criptografia e as URIs OAuth2 apontam para o recurso do homelab (keycloak.lab.home). Mais abaixo disponibilizo o script para criar os containers do Keycloak e o seu banco de dados. O broker do Kafka e o Jaeger também rodam na mesma infra.
É preciso configurar as ROLES [CLIENTE, OPERADOR, GERENTE, ADMIN, SERVICO_INTERNO] no provedor de autenticação e expô-las na claim roles do token.
Importante: salvar o arquivo .env na raiz do projeto para que o docker compose consiga lê-lo automaticamente ou definir as variáveis na sua IDE. O docker-compose.yml atual sobe a aplicação e o PostgreSQL, mas repassa ao container apenas banco, OAuth2, criptografia, rate limit e antifraude; para executar via compose com Kafka habilitado, inclua as variáveis Kafka no bloco environment da app. Se não for usar Kafka no compose, inclua EVENTOS_KAFKA_ENABLED=false no environment da app, pois o perfil dev habilita Kafka por padrão. É fundamental adicionar o arquivo .env no .gitignore para não versioná-lo.
POSTGRES_DB=transacaodb
DB_USERNAME=ozzy
DB_PASSWORD=ozzy
OAUTH2_JWKS_URI=https://keycloak.lab.home/realms/bancario/protocol/openid-connect/certs
OAUTH2_ISSUER_URI=https://keycloak.lab.home/realms/bancario
APP_CRIPTOGRAFIA_CHAVE=MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=
RATE_LIMIT_REQUISICOES_POR_MINUTO=60
ANTIFRAUDE_LIMITE_APROVACAO_AUTOMATICA=10000.00
APP_HMAC_CHAVE=xK9mP2vL8nQ5rT1wY4zB7cF3hJ6kU8aR0dG2hN5pS7vL9m
EVENTOS_KAFKA_ENABLED=true
EVENTOS_KAFKA_DLQ_MAX_TENTATIVAS=3
EVENTOS_KAFKA_DLQ_INTERVALO_REPROCESSAMENTO=10s
EVENTOS_OUTBOX_LOTE_PUBLICACAO=50
EVENTOS_OUTBOX_INTERVALO_PUBLICACAO_MS=5000
EVENTOS_OUTBOX_INTERVALO_REPROCESSAMENTO=30s
EVENTOS_OUTBOX_TIMEOUT_ENVIO_MS=5000
KAFKA_BOOTSTRAP_SERVERS=kafka.lab.home:9094
KAFKA_USERNAME=app-prod
KAFKA_PASSWORD=trocar-esta-senha
KAFKA_SSL_TRUSTSTORE_LOCATION=file:/<CAMINHO_DO_CERTIFICADO>/kafka-client-truststore.p12
KAFKA_SSL_TRUSTSTORE_PASSWORD=changeit
KAFKA_SSL_TRUSTSTORE_TYPE=PKCS12Para execução local apontando para o broker do homelab, as variáveis mínimas complementares são:
EVENTOS_KAFKA_ENABLED=true
KAFKA_BOOTSTRAP_SERVERS=kafka.lab.home:9094
KAFKA_PASSWORD=trocar-esta-senha
KAFKA_SSL_TRUSTSTORE_LOCATION=file:/<CAMINHO_DO_CERTIFICADO>/kafka-client-truststore.p12
KAFKA_SSL_TRUSTSTORE_PASSWORD=changeit
KAFKA_SSL_TRUSTSTORE_TYPE=PKCS12
KAFKA_USERNAME=app-prod# Clonar o repositório
git clone <url-do-repositorio>
cd transaction-processing-api
# Executar com perfil de desenvolvimento
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev# Build sem execução de testes
./mvnw clean package -DskipTests
# Build com execução de testes
./mvnw clean packageO artefato gerado fica no formato target/transaction-processing-<versao>.jar e, atualmente, é target/transaction-processing-0.0.1-SNAPSHOT.jar.
# Executar todos os testes
./mvnw test
# Executar teste específico
./mvnw test -Dtest=TransactionProcessingApiApplicationTests
# Executar verificação com gates de cobertura JaCoCo
./mvnw verify
# Gerar relatórios de cobertura sem falhar no gate local
./mvnw test -PcoverageImportante: Se não for utilizar uma unidade certificadora local para HTTPS, comentar ou remover as linhas abaixo do Dockerfile.
# === COMENTAR OU REMOVER ===
# USADO PARA IMPORTAR O CERTIFICADO PARA A JVM E EVITAR QUE ERRO DE CONFIABILIDADE NAS REQUISIÇÕES PARA O KEYCLOAK
# Copia o certificado da unidade certificadora para o container
COPY certificados/root_ca.crt /tmp/root_ca.crt
# Importa no truststore da JVM
RUN keytool -importcert \
-noprompt \
-trustcacerts \
-alias root-ca \
-file /tmp/root_ca.crt \
-keystore $JAVA_HOME/lib/security/cacerts \
-storepass changeit
# docker compose v2
docker compose build --no-cache
docker compose up -d
Importante: O SQL abaixo é disponibilizado para quem precisar popular a base com dados iniciais. Mantenha um arquivo local seed.sql com esse conteúdo para uso manual quando necessário. O diretório /seed-db está no .gitignore, portanto esse arquivo não deve ser versionado.
# abra o terminal no diretório do arquivo seed.sql
# envia o arquivo para a pasta /tmp/ do container
docker cp seed.sql transacao_postgres:/tmp/seed.sql
-- ============================================================
-- Executar o script considerando usuário e banco do .env
-- ============================================================
# entrar no container e executar o script
docker exec -it transacao_postgres /bin/sh
psql -U ozzy -d transacaodb -f /tmp/seed.sql
# ou em um só comando
docker exec transacao_postgres psql -U ozzy -d transacaodb -f /tmp/seed.sql-- ============================================================
-- SEED — Transaction Processing API — Fase 1
-- Executar após as migrations do Flyway terem criado as tabelas
-- ============================================================
-- IDs fixos para uso direto nos testes via curl
-- Alice → ATIVA, saldo R$ 10.000 (fluxos normais + limite)
-- Bob → ATIVA, saldo R$ 100 (cenário saldo insuficiente)
-- Carol → BLOQUEADA, saldo R$ 2.000 (cenário conta inativa)
-- -------------------------------------------------------
-- CONTAS
-- -------------------------------------------------------
-- CONTAS
-- agencia: 0001
-- 1 - número conta: 00001-9
-- 2 - número conta: 00002-7
-- 3 - número conta: 00003-5
-- -------------------------------------------------------
INSERT INTO public.conta (id, agencia, agencia_hmac, criado_em, id_cliente, numero_conta, numero_conta_hmac, status, tipo)
VALUES
('aaaaaaaa-0000-0000-0000-000000000001', 'ndmQxuQoMUN8023lOFhONmW+cTAwTG/ob04lM9i8WQk=', '4f3baf8df9c85e5a9d56f3df7f6c4a9efdb8f3c13f3d0b4c4cbfdcf05a4a9c0d', NOW(), '11111111-0000-0000-0000-000000000001', 'Gp9UEf+hVdymVh+gxKDVI6v9dD2n6XfOPY+duIq5rTCt2Xc=', '74e38e5e3a9e1f61d36f22e6b59c1f7b33a2db18f4d0a4d876c0a1d8a2d3f2e9', 'ATIVA', 'CORRENTE'),
('bbbbbbbb-0000-0000-0000-000000000002', 'ndmQxuQoMUN8023lOFhONmW+cTAwTG/ob04lM9i8WQk=', '4f3baf8df9c85e5a9d56f3df7f6c4a9efdb8f3c13f3d0b4c4cbfdcf05a4a9c0d', NOW(), '22222222-0000-0000-0000-000000000002', 'cawdBLoH00mTeqODJhn9Dh3iAZfbUcbeP4VdkA7pAwATP2U=', '2dcf0d48f7a2a4bb3f59cb34d1e08c89f0cf57c65bc46a52f9b2f67a15a42a87', 'ATIVA', 'CORRENTE'),
('cccccccc-0000-0000-0000-000000000003', 'ndmQxuQoMUN8023lOFhONmW+cTAwTG/ob04lM9i8WQk=', '4f3baf8df9c85e5a9d56f3df7f6c4a9efdb8f3c13f3d0b4c4cbfdcf05a4a9c0d', NOW(), '33333333-0000-0000-0000-000000000003', 'GY+ApqCbh1PBcfZ8OGr+x7x33X6OREvAYV+Njd2d+PuHJ3E=', '6f81db6c2ecfd0f3d0a7a3cb8f0dfd8cb4eec0b02a9e67b09cf4f4aef16cb8a3', 'BLOQUEADA', 'CORRENTE');
-- -------------------------------------------------------
-- SALDOS
-- -------------------------------------------------------
INSERT INTO public.saldo (id, atualizado_em, bloqueado, disponivel, id_conta, versao)
VALUES
('a1a1a1a1-0000-0000-0000-000000000001', NOW(), 0.00, 10000.00, 'aaaaaaaa-0000-0000-0000-000000000001', 0),
('b2b2b2b2-0000-0000-0000-000000000002', NOW(), 0.00, 100.00, 'bbbbbbbb-0000-0000-0000-000000000002', 0),
('c3c3c3c3-0000-0000-0000-000000000003', NOW(), 0.00, 2000.00, 'cccccccc-0000-0000-0000-000000000003', 0);
-- -------------------------------------------------------
-- LIMITES TRANSACIONAIS
-- limite_utilizado = limite por transação (campo mal nomeado na DDL)
-- limite_diario = teto acumulado no dia
-- utilizado_hoje = quanto já foi consumido hoje
--
-- Alice — limites normais, zerado hoje
-- Bob — limites normais, zerado hoje (saldo é o gargalo)
-- Carol — bloqueada, limites irrelevantes mas necessários para consistência
-- -------------------------------------------------------
-- Alice — PIX: até R$ 2.000 por transação, R$ 5.000/dia
INSERT INTO public.limite (id, data_referencia, id_conta, limite_diario, limite_utilizado, tipo, utilizado_hoje)
VALUES ('a1000001-0000-0000-0000-000000000001', CURRENT_DATE, 'aaaaaaaa-0000-0000-0000-000000000001', 5000.00, 2000.00, 'PIX', 0.00);
-- Alice — TED: até R$ 5.000 por transação, R$ 10.000/dia
INSERT INTO public.limite (id, data_referencia, id_conta, limite_diario, limite_utilizado, tipo, utilizado_hoje)
VALUES ('a1000002-0000-0000-0000-000000000001', CURRENT_DATE, 'aaaaaaaa-0000-0000-0000-000000000001', 10000.00, 5000.00, 'TED', 0.00);
-- Alice — TEF: até R$ 500 por transação, R$ 1.500/dia
INSERT INTO public.limite (id, data_referencia, id_conta, limite_diario, limite_utilizado, tipo, utilizado_hoje)
VALUES ('a1000003-0000-0000-0000-000000000001', CURRENT_DATE, 'aaaaaaaa-0000-0000-0000-000000000001', 1500.00, 500.00, 'TEF', 0.00);
-- Bob — PIX
INSERT INTO public.limite (id, data_referencia, id_conta, limite_diario, limite_utilizado, tipo, utilizado_hoje)
VALUES ('b2000001-0000-0000-0000-000000000002', CURRENT_DATE, 'bbbbbbbb-0000-0000-0000-000000000002', 5000.00, 2000.00, 'PIX', 0.00);
-- Bob — TED
INSERT INTO public.limite (id, data_referencia, id_conta, limite_diario, limite_utilizado, tipo, utilizado_hoje)
VALUES ('b2000002-0000-0000-0000-000000000002', CURRENT_DATE, 'bbbbbbbb-0000-0000-0000-000000000002', 10000.00, 5000.00, 'TED', 0.00);
-- Bob — TEF
INSERT INTO public.limite (id, data_referencia, id_conta, limite_diario, limite_utilizado, tipo, utilizado_hoje)
VALUES ('b2000003-0000-0000-0000-000000000002', CURRENT_DATE, 'bbbbbbbb-0000-0000-0000-000000000002', 1500.00, 500.00, 'TEF', 0.00);
-- Carol — PIX (bloqueada, mas inserção mantém consistência)
INSERT INTO public.limite (id, data_referencia, id_conta, limite_diario, limite_utilizado, tipo, utilizado_hoje)
VALUES ('c3000001-0000-0000-0000-000000000003', CURRENT_DATE, 'cccccccc-0000-0000-0000-000000000003', 5000.00, 2000.00, 'PIX', 0.00);
-- VERIFICAÇÃO
select c.id as idconta,
c.numero_conta,
c.status as status_conta,
s.disponivel,
s.bloqueado,
l.tipo as limite_tipo,
l.limite_utilizado as limite_por_transacao,
l.limite_diario,
l.utilizado_hoje
from conta c
join saldo s on s.id_conta = c.id
join limite l on l.id_conta = c.id
order by c.numero_conta, l.tipo;
.homelab
├── arcane
│ └── docker-compose.yml
├── check-certs.sh
├── database
│ └── docker-compose.yml
├── docs
│ └── gitlab-access-context.md
├── gitlab
│ ├── backup.sh
│ ├── config
│ ├── data
│ ├── docker-compose.yml
│ └── logs
├── gitlab-runner
│ ├── config
│ └── docker-compose.yml
├── infra-down.sh
├── infra-up.sh
├── jaeger
│ └── docker-compose.yml
├── k8s-states
│ ├── myblog-dev.txt
│ └── myblog.txt
├── kafka
│ ├── atualizar-credenciais-usuario-broker.txt
│ ├── certs
│ ├── docker-compose.yml
│ ├── generate-kafka-certs.sh
│ └── kafka_server_jaas.conf
├── keycloak
│ ├── backups
│ ├── docker-compose.yml
│ ├── keycloak
│ ├── keycloak-down.sh
│ ├── keycloak-up.sh
│ └── postgres
├── README.md
├── step-ca
│ ├── data
│ ├── docker-compose.yml
│ └── root_ca.crt
└── traefik
├── certs
├── config
├── docker-compose.yml
└── logs
A variável KC_HOSTNAME: https://keycloak.lab.home define o host do keycloak.
Pode ser diferente dependendo de como o serviço será provido. O mesmo vale para as labels do traefik.
É claro que pode ser utilizado um provedor externo para autenticação via OAuth2/JWT.
services:
postgres-keycloak:
image: postgres:17
container_name: postgres-keycloak
restart: unless-stopped
networks:
- infra-net
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres-keycloak-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 10
keycloak:
image: quay.io/keycloak/keycloak:26.2
container_name: keycloak
restart: unless-stopped
depends_on:
postgres-keycloak:
condition: service_healthy
networks:
- infra-net
healthcheck:
test:
[
"CMD-SHELL",
"exec 3<>/dev/tcp/localhost/9000 && echo -e 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q '200 OK'",
]
interval: 15s
timeout: 5s
retries: 20
start_period: 90s
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_ADMIN}
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres-keycloak:5432/${POSTGRES_DB}
KC_DB_USERNAME: ${POSTGRES_USER}
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
KC_HOSTNAME: https://keycloak.lab.home
KC_HOSTNAME_STRICT: "false"
KC_HTTP_ENABLED: "true"
KC_HTTP_PORT: "8080"
KC_PROXY_HEADERS: xforwarded
KC_HEALTH_ENABLED: "true"
KC_METRICS_ENABLED: "true"
command:
- start
volumes:
- ./keycloak/import:/opt/keycloak/data/import
- ./keycloak/themes:/opt/keycloak/themes
labels:
- "traefik.enable=true"
- "traefik.http.routers.keycloak.rule=Host(`keycloak.lab.home`)"
- "traefik.http.routers.keycloak.entrypoints=websecure"
- "traefik.http.routers.keycloak.tls=true"
- "traefik.http.routers.keycloak.tls.certresolver=step-ca"
- "traefik.http.routers.keycloak.service=keycloak-svc"
- "traefik.http.routers.keycloak.middlewares=rate-limit@file"
- "traefik.http.services.keycloak-svc.loadbalancer.server.port=8080"
volumes:
postgres-keycloak-data:
networks:
infra-net:
external: trueO Jaeger recebe traces via OTLP HTTP em https://otlp-jaeger.lab.home/v1/traces e expõe a interface web em https://jaeger.lab.home.
services:
jaeger-tracing:
image: jaegertracing/all-in-one:1.76.0
container_name: jaeger
restart: unless-stopped
networks:
- infra-net
environment:
COLLECTOR_OTLP_ENABLED: "true"
labels:
- "traefik.enable=true"
- "traefik.docker.network=infra-net"
# UI Jaeger: https://jaeger.lab.home
- "traefik.http.routers.jaeger-ui.rule=Host(`jaeger.lab.home`)"
- "traefik.http.routers.jaeger-ui.entrypoints=websecure"
- "traefik.http.routers.jaeger-ui.tls=true"
- "traefik.http.routers.jaeger-ui.tls.certresolver=step-ca"
- "traefik.http.routers.jaeger-ui.middlewares=rate-limit@file"
- "traefik.http.routers.jaeger-ui.service=jaeger-ui-svc"
- "traefik.http.services.jaeger-ui-svc.loadbalancer.server.port=16686"
# OTLP HTTP collector: https://otlp-jaeger.lab.home/v1/traces
- "traefik.http.routers.jaeger-otlp.rule=Host(`otlp-jaeger.lab.home`)"
- "traefik.http.routers.jaeger-otlp.entrypoints=websecure"
- "traefik.http.routers.jaeger-otlp.tls=true"
- "traefik.http.routers.jaeger-otlp.tls.certresolver=step-ca"
- "traefik.http.routers.jaeger-otlp.service=jaeger-otlp-svc"
- "traefik.http.services.jaeger-otlp-svc.loadbalancer.server.port=4318"
networks:
infra-net:
external: trueservices:
kafka:
image: ${KAFKA_IMAGE}
container_name: kafka
restart: unless-stopped
networks:
- infra-net
environment:
KAFKA_NODE_ID: ${KAFKA_NODE_ID}
KAFKA_PROCESS_ROLES: ${KAFKA_PROCESS_ROLES}
KAFKA_LISTENERS: ${KAFKA_LISTENERS}
KAFKA_ADVERTISED_LISTENERS: ${KAFKA_ADVERTISED_LISTENERS}
KAFKA_INTER_BROKER_LISTENER_NAME: ${KAFKA_INTER_BROKER_LISTENER_NAME}
KAFKA_CONTROLLER_LISTENER_NAMES: ${KAFKA_CONTROLLER_LISTENER_NAMES}
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: ${KAFKA_LISTENER_SECURITY_PROTOCOL_MAP}
KAFKA_CONTROLLER_QUORUM_VOTERS: ${KAFKA_CONTROLLER_QUORUM_VOTERS}
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR}
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR}
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: ${KAFKA_TRANSACTION_STATE_LOG_MIN_ISR}
KAFKA_AUTO_CREATE_TOPICS_ENABLE: ${KAFKA_AUTO_CREATE_TOPICS_ENABLE}
KAFKA_LOG_DIRS: ${KAFKA_LOG_DIRS}
KAFKA_SSL_KEYSTORE_TYPE: ${KAFKA_SSL_KEYSTORE_TYPE}
KAFKA_SSL_KEYSTORE_FILENAME: ${KAFKA_SSL_KEYSTORE_FILENAME}
KAFKA_SSL_KEYSTORE_CREDENTIALS: ${KAFKA_SSL_KEYSTORE_CREDENTIALS}
KAFKA_SSL_KEY_CREDENTIALS: ${KAFKA_SSL_KEY_CREDENTIALS}
KAFKA_SSL_TRUSTSTORE_TYPE: ${KAFKA_SSL_TRUSTSTORE_TYPE}
KAFKA_SSL_TRUSTSTORE_FILENAME: ${KAFKA_SSL_TRUSTSTORE_FILENAME}
KAFKA_SSL_TRUSTSTORE_CREDENTIALS: ${KAFKA_SSL_TRUSTSTORE_CREDENTIALS}
KAFKA_SSL_CLIENT_AUTH: ${KAFKA_SSL_CLIENT_AUTH}
KAFKA_SASL_ENABLED_MECHANISMS: ${KAFKA_SASL_ENABLED_MECHANISMS}
KAFKA_OPTS: ${KAFKA_OPTS}
volumes:
- ${KAFKA_DATA_VOLUME}:/var/lib/kafka/data
- ./certs:/etc/kafka/secrets:ro
- ./kafka_server_jaas.conf:/etc/kafka/kafka_server_jaas.conf:ro
healthcheck:
test:
[
"CMD-SHELL",
"bash -ec 'echo > /dev/tcp/127.0.0.1/${KAFKA_HEALTHCHECK_PORT}'",
]
interval: ${KAFKA_HEALTHCHECK_INTERVAL}
timeout: ${KAFKA_HEALTHCHECK_TIMEOUT}
retries: ${KAFKA_HEALTHCHECK_RETRIES}
start_period: ${KAFKA_HEALTHCHECK_START_PERIOD}
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.kafka.rule=HostSNI(`kafka.lab.home`)"
- "traefik.tcp.routers.kafka.entrypoints=kafkassl"
- "traefik.tcp.routers.kafka.tls=true"
- "traefik.tcp.routers.kafka.tls.passthrough=true"
- "traefik.tcp.services.kafka.loadbalancer.server.port=9094"
volumes:
kafka-data:
networks:
infra-net:
external: trueKAFKA_IMAGE=apache/kafka:3.9.0
KAFKA_NODE_ID=1
KAFKA_PROCESS_ROLES=broker,controller
KAFKA_LISTENERS=INTERNAL://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093,SASL_SSL://0.0.0.0:9094
KAFKA_ADVERTISED_LISTENERS=INTERNAL://kafka:9092,SASL_SSL://kafka.lab.home:9094,CONTROLLER://kafka:9093
KAFKA_INTER_BROKER_LISTENER_NAME=INTERNAL
KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,INTERNAL:PLAINTEXT,SASL_SSL:SASL_SSL
KAFKA_CONTROLLER_QUORUM_VOTERS=1@kafka:9093
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1
KAFKA_AUTO_CREATE_TOPICS_ENABLE=true
KAFKA_LOG_DIRS=/var/lib/kafka/data
KAFKA_DATA_VOLUME=kafka-data
KAFKA_SSL_KEYSTORE_TYPE=PKCS12
KAFKA_SSL_KEYSTORE_FILENAME=kafka.keystore.p12
KAFKA_SSL_KEYSTORE_CREDENTIALS=kafka_ssl_keystore_creds
KAFKA_SSL_KEY_CREDENTIALS=kafka_ssl_key_creds
KAFKA_SSL_TRUSTSTORE_TYPE=PKCS12
KAFKA_SSL_TRUSTSTORE_FILENAME=kafka.truststore.p12
KAFKA_SSL_TRUSTSTORE_CREDENTIALS=kafka_ssl_truststore_creds
KAFKA_SSL_CLIENT_AUTH=none
KAFKA_SASL_ENABLED_MECHANISMS=SCRAM-SHA-256
KAFKA_OPTS=-Djava.security.auth.login.config=/etc/kafka/kafka_server_jaas.conf
KAFKA_HEALTHCHECK_PORT=9092
KAFKA_HEALTHCHECK_INTERVAL=15s
KAFKA_HEALTHCHECK_TIMEOUT=10s
KAFKA_HEALTHCHECK_RETRIES=5
KAFKA_HEALTHCHECK_START_PERIOD=30s
KAFKA_CERT_CN=kafka.lab.home
KAFKA_CERT_SAN_DOCKER=kafka
KAFKA_CERT_TTL=720h
STEP_CA_ROOT_CERT=../step-ca/root_ca.crt
STEP_CA_INTERMEDIATE_CERT=../step-ca/data/certs/intermediate_ca.crt
STEP_CA_INTERMEDIATE_KEY=../step-ca/data/secrets/intermediate_ca_key
STEP_CA_PASSWORD_FILE=../step-ca/data/secrets/password.txt
KAFKA_SSL_SECRET=changeit
KAFKA_SASL_USERNAME=app-prod
KAFKA_SASL_PASSWORD=trocar-esta-senhaKEYCLOAK_ADMIN=administrador
KEYCLOAK_ADMIN_PASSWORD=Adm123*
POSTGRES_DB=keycloak
POSTGRES_USER=keycloak
POSTGRES_PASSWORD=keycloak
| Tecnologia | Finalidade |
|---|---|
spring-boot-starter-web |
API REST com Spring MVC |
spring-boot-starter-data-jpa |
Persistência via Hibernate/JPA |
spring-boot-starter-security |
Framework de segurança |
spring-boot-starter-oauth2-resource-server |
Validação de JWT OAuth2 |
spring-boot-starter-validation |
Bean Validation (Jakarta Validation) |
spring-boot-starter-actuator |
Health checks e endpoints operacionais |
micrometer-registry-prometheus |
Exportação de métricas Prometheus |
spring-boot-starter-opentelemetry |
Auto-configuração de tracing OpenTelemetry |
micrometer-tracing-bridge-otel |
Bridge Micrometer Tracing para OTel |
opentelemetry-exporter-otlp |
Exportador OTLP para traces |
spring-boot-starter-json |
Serialização Jackson 3 |
spring-boot-starter-kafka |
Publicação e consumo de eventos Kafka |
springdoc-openapi-starter-webmvc-ui |
Documentação OpenAPI / Swagger UI |
postgresql |
Driver JDBC PostgreSQL |
flyway-core + flyway-database-postgresql |
Versionamento e migração de schema |
bucket4j_jdk17-core |
Rate limiting via token bucket |
logstash-logback-encoder |
Logs estruturados em JSON (Logstash) |
jacoco-maven-plugin |
Relatórios e validação de cobertura |
spring-boot-testcontainers |
Integração Spring Boot + Testcontainers |
testcontainers-postgresql |
PostgreSQL em testes de integração |
testcontainers-kafka |
Kafka em testes de integração |
archunit-junit5 |
Testes de regras arquiteturais |
h2 |
Banco em memória para testes |
- Criar tabela
outbox_eventono banco de dados - Implementar
OutboxEventoEntitye adaptador JPA - Implementar publicador agendado (Outbox Publisher)
- Adicionar dependência Spring Kafka ao
pom.xml - Implementar produtores Kafka para eventos de domínio (
transacoes.iniciadas,transacoes.concluidas,transacoes.falhas,transacoes.estornadas) - Implementar lógica de negócio nos consumidores Kafka para PIX, TED e TEF (
PixTransacaoKafkaConsumidor,TedTransacaoKafkaConsumidor,TefTransacaoKafkaConsumidor) - Implementar Dead Letter Queue com reprocessamento configurável
- Implementar Domain Events:
TransacaoIniciadaEvento,TransacaoConcluidaEvento,TransacaoFalhouEvento,TransacaoEstornadaEvento - Implementar idempotência de consumidor via tabela
evento_processado
- Implementar
AttributeConverterJPA para criptografia em repouso dos campos sensíveis (CriptografiaConverter— AES-256-GCM) - Implementar filtro de mascaramento de dados sensíveis nos logs (
observabilidade/mascaramento+LogMascaramentoConverter) - Configurar integração com HashiCorp Vault ou AWS Secrets Manager para gestão de segredos
- Instrumentar serviços com métricas customizadas Micrometer (contadores, timers, gauges)
- Adicionar dependência
micrometer-tracing-bridge-otel - Configurar exportação de traces via OTLP para Jaeger (dev) ou Grafana Tempo (prod)
- Configurar logs estruturados em JSON (
logstash-logback-encoder+JsonMascaradoProviderintegrado ao Logback) - Configurar dashboards Grafana com alertas de SLO
- Adicionar dependência Resilience4j ao
pom.xml - Implementar Circuit Breaker para SPI, STR, DICT e antifraude
- Implementar Retry com backoff exponencial e jitter por integração
- Implementar Bulkhead para isolamento de recursos por tipo de transação
- Implementar Timeout configurável por integração
- Implementar testes unitários de segurança (
HmacServiceTest,HmacUtilsTest,CriptografiaConverterTest) - Implementar testes de integração de criptografia (
CriptografiaConverterIntegrationTest) - Implementar testes unitários de domínio (
TransacaoTest,TransacaoEventosTest,ContaTest,SaldoTest,LimiteTransacionalTest,ValorMonetarioTest) - Implementar testes unitários de serviços de aplicação (
ProcessaTransacaoServiceTest,CriaTransacaoServiceTest,ConsultaTransacaoServiceTest,EstornoTransacaoServiceTest,LimiteServiceTest,SaldoServiceTest) - Implementar testes unitários de estratégias (
PixTransacaoStrategyTest,TedTransacaoStrategyTest,TefTransacaoStrategyTest,StrategyResolverTest) - Implementar testes de integração com Testcontainers (
IntegrationTestBase,ProcessaTransacaoIntegrationTest,EstornoIntegrationTest,SaldoIntegrationTest,OutboxEventoIntegrationTest) - Implementar testes de métricas (
TransacaoMetricasTest,OutboxMetricasTest) - Implementar testes arquiteturais com ArchUnit (
ArquiteturaHexagonalTest,CamadaSegurancaTest,NamingConventionTest) - Configurar relatório de cobertura de código (JaCoCo)