Documentação Completa — Integrador¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-10
O desenho do sistema no estado atual: o que é, como os dados fluem do ERP até o celular, quais são as entidades e como se autentica. Runbooks de operação estão em Operação; detalhes de código, em Dev; o histórico das decisões, em Decisões.
Sem manual de usuário — por quê
O Integrador é um serviço de integração backend (API REST + telas administrativas
internas). Não há docs/manual/ (manual de usuário final) de propósito: o produto voltado
ao usuário é o app móvel (repositório próprio), que consome este serviço via PowerSync/API.
A experiência de usuário final é documentada lá, não aqui.
Componentes¶
- X-Adm — ERP de mesa (base Zim) onde todos os dados nascem: o sistema-fonte da integração.
- X-Adm Integração Java — agente on-premises que lê os arquivos JSON exportados pelo X-Adm e os encaminha via REST/HTTPS (com retry) ao Integrador.
- Integrador — este repositório: servidor Micronaut / Java 25, uma instância por cliente (subdomínio por tenant atrás do Coolify/Traefik, porta 8080). Recebe REST, persiste no Postgres, emite push (FCM) e alimenta o PowerSync.
- PostgreSQL por cliente — banco dedicado por tenant (Postgres 18+,
uuidv7). - PowerSync (self-hosted) — Docker Compose por cliente
(
powersync/<cliente>/), cada um com seu MongoDB; lê o WAL do Postgres (replicação lógica, publicaçãopowersync) e replica para o SQLite do app. - central-backend (
central-backend.xadm.biz;auth.xadm.bizé o nome antigo, mantido como issuer/JWKS) — serviço central da casa: autenticação + JWKS (o Integrador não emite JWT próprio — ver decisão 0001) e destino do heartbeat do integrador-client que esta instância repassa (§ Heartbeat). - integrador-client (repositório próprio) — jar on-premise do cliente que faz o write-back pelo PowerSync e manda o heartbeat a esta instância (docs).
- FCM (Firebase Admin Java) — push para o aplicativo.
- Aplicativo Flutter (repositório separado) — SQLite/Drift local via PowerSync; mescla empresas no cliente.
Fluxo de dados¶
- O usuário age no X-Adm → o X-Adm gera um arquivo JSON, marcado com um código
de Origem (
PPEDAMX,PPEDIDO,PBLDI,PNOTAI,PNOTAD,PCSTPROD,PREQUIS). - O agente X-Adm Integração Java envia a
PUT/DELETE /api/v1/xadmcomAuthorization: Bearer <INTEGRADOR_API_TOKEN>. (Esse é o fluxo de saída, X-Adm → apps. Há também o fluxo de entrada — fonte externa PIED,Origem = INT-PIED, dado que volta ao X-Adm — no mesmo endpoint; ver decisão 0013.) - O Integrador faz o parse e persiste no Postgres. Para JSON válido o
endpoint sempre responde HTTP 200; o resultado vai no corpo
(
{requestId, resultado: SUCESSO|ERRO, mensagem}).HTTP 400só para JSON malformado. Detalhe em tratamento de erros. - Todo apply do XADM é serializado por um
ReentrantLock(fair=true)global, para preservar a ordemDELETE→PUTdo ERP e evitar deadlock no Postgres (40P01). Cada requisição é auditada na tabelarequest. - O PowerSync acompanha o WAL e replica para o SQLite do celular (offline-first).
- Em notas de venda/entrada, o Integrador envia push FCM (ver payload FCM).
- Checagem de "banco parado": o app consulta o endpoint público
GET /api/v1/powersync/lastchange.
Heartbeat do integrador-client¶
Uma instalação do integrador-client que para (máquina desligada, jar travado) só aparecia horas depois, como "a integração parou". O heartbeat torna isso visível sem acesso à máquina, passando por esta instância — o único destino que o firewall do cliente já libera:
- O integrador-client manda
POST /api/v1/heartbeat(versão, fluxos, hostname) no startup e a cada 60 min, com o mesmo BearerINTEGRADOR_API_TOKENdo write-back. - Esta instância valida o corpo, carimba o
cliente_id(envCLIENTE_ID— identidade do contexto, nunca do corpo) e repassa ao central-backend emPOST {CENTRAL_URL}/api/integrador/heartbeat, BearerCENTRAL_API_TOKEN. Não grava nada aqui. - O central registra a instalação, alerta no GlitchTip quando ela passa de 6 horas úteis sem sinal e a mostra na aba Deploy do central-ui.
Contrato do salto client → aqui: heartbeat. O porquê (rota em dois saltos, contrato congelado, alerta por horas úteis): a decisão local 0028 do central-backend. Configuração: heartbeat na configuração.
Modelo de dados¶
As entidades de negócio e as tabelas de infraestrutura (request,
pushenviada, pabast_*) estão descritas em
Modelagem de dados. Toda tabela de negócio carrega a chave de
negócio do X-Adm e um id UUID v7 surrogate, além de
soft-delete (deleted, deleted_at).
Modelo de autenticação¶
/api/**→ Bearer estático (INTEGRADOR_API_TOKENno ambiente; sem fallback para o antigoAPI_BEARER_TOKEN); 401 se errado. Exceção pública:GET /api/v1/powersync/lastchange(sem PII, exigido no cold boot do app). Ver Segurança e /health.- Views server-side (
/, MVC server-render JTE) → login Google/Firebase (domínio@xadm.com.br),SessionAuthenticationFetcher(libxadm-seguranca) +ViewSecurityRule(policy local, micronaut-security) + cookiexadm_session(JWT HS256, 8h) e CSRF double-submit./login,/health,/swagger/**públicos. Ver login Google e decisão 0005. - Nenhum JWT é emitido por este JAR (o login/JWT do pAbast foi removido — ver
decisão 0001); o JWKS do PowerSync aponta
para
auth.xadm.biz. - Botões de edição da UI e
/debugsó ligam sob os perfisdev/test; em produção a UI é somente-leitura. Ver perfis Micronaut.
Configuração¶
application.yml (defaults de produção) + ENV do Coolify; application-dev.yml
só com MICRONAUT_ENVIRONMENTS=dev (Postgres local :5432, Flyway clean-schema);
testes usam application-test.yml. Não há application-prod.yml. O Sentry só
inicializa com DSN válido em ambiente não-dev/test. Detalhe em
Configuração.