Pular para conteúdo

Guia do código

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-15

Audiência: dev que abriu o repositório e quer passar os olhos e entender como o código está organizado, antes de mergulhar classe a classe. Para o porquê das decisões, veja decisoes/; para o desenho completo, a Documentação Completa; para os contratos da PIED, API da PIED.

O retrato em uma frase

maxsul-pied é a Fase 1 da integração PIED → X-Adm: um servidor Micronaut que só captura o raw da PIED por dois canais (webhook + poll REST) e o persiste cru em três tabelas landing. Não transforma nem escreve no X-Adm — isso é a Fase 2, que nasce desligada por toggle.

flowchart TB
  PIED[PIED]
  PIED -->|POST /webhook/pied| WH[webhook: WebhookController]
  PIED -->|GET REST paginado| POLL[poll: PollJob → PollService → PiedClient]
  WH --> PW[(pied_webhook)]
  POLL --> PR[(pied_rest)]
  POLL --> PC[(pied_cursor)]

Como o código é organizado

Organização package-by-feature: cada canal é um pacote de topo que carrega suas próprias camadas, em vez de pastas globais controller//service/. O transversal vive em comum. Base: br.com.xadm.maxsul.pied.

Pacote É Responsabilidade
webhook feature recebe POST /webhook/pied, valida toggle/segredo, grava cru em pied_webhook.
poll feature job agendado que pagina a API REST da PIED e grava cada página em pied_rest (+ cursor).
normalizado feature deriva as tabelas normalizadas (pied_pedido/pied_cliente/pied_produto) do raw capturado.
integracao feature Fase 2: transform → push ao Integrador, console do operador, reconciliação. Toggle IntegracaoConfig (decisão 0001), modo PRODUCAO/STAGING (decisão 0004).
painel feature painel do colaborador Maxsul: dashboard por bucket de status + detalhe do pedido (decisão 0006).
alerta feature destinatários + envio do e-mail de erro terminal via Resend (decisão 0005).
dados feature browser read-only das tabelas pied_* + índice de operador (/homedev).
diagnostico feature captura crua (timeline/dump) e o smoke-test do GlitchTip.
retencao feature job de expurgo do raw por janela de retenção.
comum base transversal local: GlobalExceptionHandler (500 + página HTML), Json, GlobalViewModel, port ModoIntegracao. A base compartilhada da casa — HealthController, UnifiedErrorResponseProcessor (RFC 7807 de ponto único), ProblemDetail, VersaoInfo — vem da lib xadm-comum-web (br.com.xadm.comum.web; decisão 0008).

As camadas

Persistência é Micronaut Data (micronaut-data-jdbc, decisão 0022): todo repositório é @JdbcRepository. O CRUD simples usa o CrudRepository; o que carrega regra — os upserts com ON CONFLICT, as transições da FSM guardadas por status, as projeções — é @Query nativo numa interface Sql aninhada na classe do repositório, que mantém a API de domínio e a transação. O corpo cru nunca é desserializado num modelo de negócio: é gravado como jsonb como chegou. A varredura do raw que alimenta a normalização segue a mesma forma — @Query nativo no RawCapturadoRepository, devolvendo a projeção RawLinha.

Em cada feature o controller é a borda HTTP e delega ao service, que é quem fala com o repositório (DadosService, CapturaService, PainelService, ConsoleService, WebhookService; os destinatários de alerta passam pelo AlertaService). Nas telas de leitura o service só repassa a consulta: a fronteira existe para o controller não depender da persistência, não para criar regra nova.

  • Webhook: WebhookController → WebhookService → PiedWebhookRepository. O controller responde 200 imediato após o insert; erro de persistência propaga → 500 (a reconciliação do poll é a rede de segurança, ver api-pied.md §3.1).
  • Poll: PollJob (@Scheduled) → PollService (orquestra a rodada) → PiedClient (HTTP, envelope, retry) + Paginador (paginação/parada) → PiedRestRepository/CursorRepository. Só pedidos usa o cursor pied_cursor como ?lastUpdateAfter, avançando só em sucesso; falha de uma entidade não derruba as demais.

Travas de arquitetura

O gate é ./gradlew check (checkstyle + testes + cobertura + fronteiras). Cinco fronteiras são guardadas por teste (arquitetura/FronteirasTest, ArchUnit), e não por convenção — poucas regras, alto sinal. As quatro primeiras vêm da fábrica RegrasArquitetura da xadm-comum-teste:

Fronteira Por que ela importa
comum não depende de nenhuma feature comum é a base que toda feature usa; se ela conhece uma feature, a seta se inverte e o pacote base vira refém do canal.
features não formam ciclo entre si cada canal tem de poder ser lido, testado e desligado em separado — o desligar-em-separado é o kill switch da decisão 0001.
controller não acessa repository o controller é a borda HTTP e delega ao service da feature, que é quem fala com a persistência; no package-by-feature os dois moram no mesmo pacote, então a fronteira é pelo tipo. A regra da lib casa GenericRepository; uma regra local casa o sufixo Repository, porque aqui o repositório é a classe que embrulha a interface Sql.
nada depende de controller domínio, jobs e services não conhecem o controller nem os DTOs aninhados nele; o controller mapeia o que o service devolve.
persistência só por Micronaut Data DataSource.getConnection() só na allowlist — hoje, o advisory lock de sessão do PiedClusterLock. Classe nova ali é decisão, com o motivo no comentário (decisão 0022).

A guarda nasceu junto com a migração para a constituição 0.24.0 e pegou violação real na primeira execução: o GlobalViewModel (em comum) injetava o IntegracaoConfig concreto, prendendo a base ao canal de integração e fechando quatro ciclos. A correção foi inverter a seta: comum declara o port ModoIntegracao (só o que o header precisa — rótulo do modo e se é staging) e o IntegracaoConfig o implementa.

Limite conhecido: a guarda lê bytecode, então dependência que exista só via constante public static final é invisível (o compilador inlina o valor). Acoplamento real — tipo em campo ou parâmetro, injeção, chamada — é pego.

A outra fronteira que importa (não persistir credencial) também é guardada por teste: WebhookControllerTest verifica que Authorization/X-Pied-Secret são gravados mascarados.

Por onde começar a ler

  1. Application.java — entrypoint Micronaut.
  2. webhook/WebhookController.java — o caminho quente mais simples, ponta a ponta.
  3. poll/PollService.java + poll/Paginador.java — a orquestração da captura REST e a paginação.
  4. V1__landing_pied.sql (Flyway) — o schema landing que tudo alimenta.

Rodar e testar

Subir o app, os dois conjuntos de teste, o padrão do teste de integração e o gate local estão em Como rodar.

Referência completa

A árvore de classes navegável (Javadoc, interna) não é publicada neste app — o job de docs do pipeline.yml não gera dev/api/ (a geração é opt-in). A narrativa acima é o atalho para achar por onde entrar.