Pular para conteúdo

Guia do código

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

Para quem vai mexer no código. O porquê das escolhas está nas decisões; os contratos com o ZIM estão em contrato pAbast. Aqui fica o mapa: onde cada coisa mora. Como rodar na sua máquina está em como rodar.

Princípio de leitura

O público deste código é a equipe que desenvolve em ZIM e sabe pouco Java. Por isso: sem framework, sem injeção de dependência, wiring manual e explícito, e duplicidade aceita em prol da leitura. Antes de extrair uma abstração "porque repete", pergunte se ela facilita a vida de quem lê — normalmente não facilita.

Exceção única de linguagem: uma classe Kotlin embrulha o SDK PowerSync (Kotlin Multiplatform, com suspend/Flow, inviável de Java 8 puro). Nenhuma lógica de negócio mora nela — só a ponte.

Mapa dos pacotes

Base: br.com.xadm.integradorclient.

Pacote Papel
(raiz) ponto de entrada e trava de instância: parsing das flags, wiring manual de tudo, guarda de uma execução por pasta de dados
config leitura e validação do .properties, com erro que nomeia a chave faltante
auth JWT auto-assinado (RS256) que autentica o cliente no PowerSync da nuvem
powersync o espelho local: declaração do schema das tabelas, o wrapper Kotlin do SDK, o write-back HTTP do status e o envio do heartbeat (EnvioHeartbeat)
processo o miolo: um processador por fluxo, o agendador serial que os roda, a coleta coerente por remessa, a máquina de status, os avisos e o heartbeat periódico (BatimentoPeriodico)
zim a fronteira com o ERP: os fluxos conhecidos, a escrita do arquivo de envio fixed-width, a leitura do retorno e a execução do zimrtmu.exe

Quatro pontos que costumam surpreender quem chega:

  • Um lote carrega as tabelas de UM fluxo só. O fluxo é que define quais tabelas viajam e qual literal de handshake o ZIM vai ler para saber o que chegou.
  • O arquivo de envio e o contrato andam juntos. Mudou uma largura de campo no layout, muda o contrato no mesmo PR — o gate não compara os dois por você.
  • O que roda a cada volta do laço não pode lançar. O Agendador chama o vigia do write-back e o heartbeat periódico sem try/catch; uma exceção ali derrubaria o daemon. Por isso EnvioHeartbeat e BatimentoPeriodico engolem até RuntimeException e só logam WARN. O BatimentoPeriodico recebe o envio como Consumer — processo não conhece o HTTP, e o teste usa um consumidor fake.
  • O nível do log decide o que abre incidente. Um LOG.warn ou LOG.error novo vira issue no GlitchTip sozinho, pelo appender do Logback — não há (nem se deve criar) chamada ao SDK no call-site. O que é ação do operador na ponta, e não falha do app, sai pelo logger …integradorclient.operador; o eco do dXp sai pelo …integradorclient.eco. Os dois ficam fora do appender, e o ObservabilidadeLogbackTest trava isso (decisão 0006).

Como compilar e testar localmente

Pré-requisitos, comandos do gate e recomendações de ambiente estão em como rodar.

Onde NÃO mexer sem ler antes

  • Layout fixed-width (zim): cada campo tem largura e escala fechadas com a equipe ZIM; os testes golden travam byte a byte. Mudança aqui é mudança de contrato.
  • Casamento envio↔retorno (processo): por posição de linha no fluxo pied, por contador no encerramento de MDF-e. Divergência invalida o lote inteiro de propósito — nunca aplique resultado desalinhado. Duas tolerâncias, ambas para defeitos do pAbast em conflito: os começos de tentativa abortada que ele deixa antes da rodada final e o eco da linha de continuação no fim do retorno (contrato §4, salvaguardas do reinício e do eco) — não as alargue sem a equipe ZIM.
  • Coleta por remessa (processo): o cliente lê as flags que o servidor calcula; ele não deduz dependência entre tabelas (decisão 0004).