0012 — Convergência da entrega ao outbox da casa e adoção do micronaut-data¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-11 · Decidido em: 2026-08-24
Contexto¶
O int-pied garantia a entrega ao Integrador com um motor de outbox próprio (bespoke): FSM em
pied_pedido.status, PushService (envio idempotente por content_hash, atômico por code via
marcarEnviando), retry/backoff à mão. A casa extraiu essa capacidade para a lib
xadm-mensageria (MensagemM2m + RelayM2m + SPI EnviadorMensagem + tela
/admin/mensageria) — dois motores de outbox na casa. A convergência é débito de uniformidade
(spec 004), não gap funcional.
Decisão¶
Converge a ENTREGA (não a FSM de negócio) para o outbox da casa; a FSM pied_pedido e a
reconciliação ficam.
- Separação domínio × entrega:
pied_pedido.statussegue a FSM de negócio; o PUSH ao Integrador vira umaMensagemM2m tipo="xadm-push". OPushEnviador(SPIEnviadorMensagem) faz o PUT; oRelayM2mda lib substitui o drain automático. - Idempotência preservada sobre at-least-once: o
RelayM2mé at-least-once sem lock; oPushEnviadorre-checa ocontent_hashantes do PUT, e o/api/v1/xadmdo Integrador é upsert idempotente por chave (confirmado noXadmController) — dupla proteção. - Entrega × negócio no PUT sempre-200: o
/api/v1/xadmresponde 200 mesmo emresultado=ERRO. Logo 200 = entrega OK (ResultadoEnvio.ok, não retenta); o resultado de negócio (SUCESSO/ERRO) vai pro domínio (marcarEnviado/marcarErro). Só falha de transporte (timeout/5xx/401) →ResultadoEnvio.falha(o relay retenta). ERRO de negócio não auto-retenta. - Prontidão fica no domínio: o guard
temInvoice(segura o envio até o REST preencher a invoice) fica no passo que enfileira — enfileira só o send-ready, sem penalizar o relay.
Consequência dura: adotar xadm-mensageria FORÇA micronaut-data¶
O MensagemM2mRepository da lib é @JdbcRepository → arrasta micronaut-data-jdbc. Com ele no
classpath, o DataSource injetado vira transaction-aware, e o int-pied — raw-SQL por design
(dataSource.getConnection() direto, "sem micronaut-data") — quebra em massa com "No current
connection present". Isso força uma migração do padrão de persistência. O padrão (comprovado
experimentalmente, 76 falhas → 0 no caminho crítico):
annotationProcessor("io.micronaut.data:micronaut-data-processor")— o fix-chave. Sem ele,@Transactionalfica não-processado (no-op): não há interceptor AOP → o acesso raw-SQL fora de transação quebra. É o que o int-pied nunca teve (nunca usou data).@Transactionalclass-level nos 13 repos raw-SQL — torna o app transaction-aware; o path atômico (marcarEnviando+enfileirar) compartilha a transação.- Nos TESTES, desembrulhar o
DataSource— o seed viadataSource.getConnection()direto é incompatível com o wrapper transaction-aware em ambos os modos de@MicronautTest(transactional=trueesconde o seed do controller;transactional=falsefaz o próprio seed falhar). O fix:while (ds instanceof DelegatingDataSource d) ds = d.getTargetDataSource();→ seed committed, visível ao app. (transactional=falseglobal, como os apps data-jdbc da casa, não basta aqui porque o seed é raw JDBC.)
Correção 2026-09-11: o "raw-SQL por design" acabou — a persistência inteira migrou para Micronaut Data (decisão 0022): os repositórios não abrem mais
dataSource.getConnection(), o SQL é@Querynativo com o mesmo texto. Seguem valendo a convergência da entrega ao outbox, oannotationProcessor(item 1) e o@Transactionalde classe nos repositórios (item 2, agora sobre a interface@JdbcRepository). O desembrulho doDataSource(item 3) continua só naBancoFixturede teste. SQL à mão ficou apenas na varredura em lote doNormalizadorServicee no advisory lock doPiedClusterLock, guardados peloFronteirasTest.
Status (2026-08-24)¶
Código completo e compilando. Gate sem-Docker verde (test unit + checkstyle +
compileIntegrationTest); WebhookControllerTest de integração passou isolado, provando o
unwrap. A suíte de integração completa não foi validada localmente — a máquina de dev degradou
após horas (worker JVM morre com EOFException), problema de ambiente, não de código. Validação
deferida ao CI (ci-java:25 limpo) ou a um check pós-reboot — decisão explícita (REGRA Nº3),
não silêncio.
Pendências declaradas¶
- Validação da suíte de integração no CI/ambiente limpo (o gate real).
- Gate de segurança
/admin/mensageria: a tela da lib nasce aberta (security.enabled=false); o gate@<dominio>fica pra quando a segurança ativar (deferida, gate de identidade TBD). - Blocking B2 (§6): o SPI
EnviadorMensagemdocumenta@Client-gerado-de-OpenAPI; o int-pied usa client à mão contra o legado/api/v1/xadm— divergência aceita; a lib deveria rotular como aceitável ou prover adaptador.
Alternativas descartadas¶
- Manter o outbox bespoke. Funciona, mas mantém dois motores na casa (débito de convergência).
@MicronautTest(transactional=false)global (padrão dos apps data-jdbc). Não resolve o seed raw-JDBC do int-pied — só o desembrulho do DataSource resolve.- Desembrulhar o DataSource na PRODUÇÃO. Quebraria a atomicidade do enfileiramento; o desembrulho é só nos testes, a produção fica transaction-aware.