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 entender como o código está organizado antes de ler classe a classe. O desenho do sistema está na Documentação Completa; o porquê da organização, nas decisões 0010, 0011 e 0012; rodar e testar, em Como rodar.
O retrato em uma frase¶
O ERP X-Adm do cliente exporta cada mudança como JSON, o agente on-premises a envia à API de ingestão
(/api/v1/xadm), o Integrador grava no Postgres da instância e o PowerSync replica o banco para o app
móvel. Em volta desse fio ficam a API CRUD /api/v1/*, as telas JTE de consulta, o push FCM das notas
e as mensagens de saída pelo outbox (watch de MDF-e para o int-sascar, poke para o tradutor da Thoms).
flowchart TB
ERP["ERP X-Adm"] -->|JSON| Agente["agente on-premises"]
Agente -->|"/api/v1/xadm"| Ingest["ingest — parser e handlers por tabela"]
Ingest --> Banco[("Postgres da instância")]
Banco --> PowerSync["PowerSync"] --> App["app móvel"]
Ingest -->|NotaAplicadaEvent| Push["push — FCM"]
Ingest -->|outbox| Webhook["webhook — watch e poke"]
Api["api — CRUD /api/v1/*"] --> Banco
Views["views — telas JTE"] --> Banco
Como o código é organizado¶
Organização package-by-feature sob br.com.xadm, sobre uma fundação de dados (core) e um kernel
(comum). O mapa abaixo é o de mais alto nível; o papel de cada pacote em detalhe, as camadas e a
escrita por chave natural estão em Estrutura de código.
| Pacote | É | Responsabilidade |
|---|---|---|
core |
base | entidades, repositórios, enums e eventos de domínio; não depende de nada do app |
comum |
base | config, regras de acesso de /api/** e das telas, erros RFC 7807, observabilidade, /health; depende só de core |
ingest |
feature-motor | ingestão do payload XADM, o manifesto de remessa e a leitura do retorno do X-Adm |
powersync |
feature-motor | rotas do PowerSync: write-back, reset e lastchange |
push |
feature-motor | push FCM disparado pela nota aplicada |
export |
feature-motor | exportação de tabela para XLS |
heartbeat |
feature-motor | repasse do heartbeat do integrador-client ao central-backend |
sascar |
feature | callback de encerramento de MDF-e vindo do int-sascar |
webhook |
feature | mensagens de saída pelo outbox da xadm-mensageria: watch de MDF-e ao int-sascar e poke ao tradutor da Thoms |
api |
edge | controllers CRUD por entidade em /api/v1/* |
views |
edge | telas JTE de consulta, a home e a tela de debug |
A infra idêntica entre apps vem das libs da casa: login e sessão da xadm-seguranca, /health e
Sentry da xadm-comum-web, outbox e relay M2M da xadm-mensageria.
As camadas¶
A ingestão entra pelo XadmController; o XadmService serializa as aplicações sob um lock e descarta
payload vazio; o XadmApplyService percorre o XadmTableRegistry e aplica cada XadmTableHandler na
ordem que respeita as chaves estrangeiras. A escrita por chave natural (upsert com revive de soft-delete)
tem um ponto único, o EscritaPorChaveNatural, que os controllers CRUD de api também usam.
Nas edges, o controller só traduz HTTP: api responde JSON e views monta o modelo e renderiza o JTE
dentro do kit/layout.jte (UI das telas). O acesso é decidido por duas regras de
comum: o ApiBearerSecurityRule exige o Bearer em /api/** e o ViewSecurityRule exige login nas
telas (Segurança).
ingest não conhece push: a nota aplicada publica um NotaAplicadaEvent (em core) e o listener de
push reage (decisão 0011).
Travas de arquitetura¶
O ArchitectureTest roda no ./gradlew check e guarda as fronteiras descritas em
Estrutura de código: core puro, comum dependendo só de core,
features-motor independentes entre si e nenhum ciclo entre pacotes. sascar e webhook não constam
das listas de pacotes do teste.
Por onde começar a ler¶
Application— inicializa o Sentry antes do Micronaut e declara o OpenAPI com o Bearer.ingest.XadmController→ingest.XadmService→ingest.XadmApplyService— o fio da ingestão.ingest.XadmTableRegistrye dois handlers: um simples (ingest.MunicipioHandler) e um com regra própria (ingest.ContratosHandler).comum.ApiBearerSecurityRuleecomum.ViewSecurityRule— quem entra em/api/**e nas telas.src/main/resources/db/migration/— o schema, migration a migration; o modelo consolidado está em Modelagem de dados.