0032 — A tela de login é servida pela lib¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-12 · Decidido em: 2026-09-08
Contexto¶
O XadmLoginController mora no xadm-seguranca desde a 0.5.0 (0019):
a lib manda no fluxo (rotas, CSRF, callback, sessão). A view, não — cada app tem a sua
src/main/jte/login.jte. Cinco apps, cinco cópias.
Caracterizando os cinco corpos — não estimando pela similaridade textual, que enganava:
| app | usa kit.layout |
marca grande + rodapé | Firebase | fluxo |
|---|---|---|---|---|
| bi-comercial-xls | sim | sim | 10.13.2 | popup |
| webstorm-ecom | sim | sim | 10.13.2 | popup |
| bi-transporte-xls | sim | sim | 10.13.2 | popup |
| int-sascar | sim | não | 10.13.2 | popup |
| integrador-server | sim | não | 10.13.2 | popup |
O mecanismo é idêntico nos cinco — mesma versão do SDK, mesmo signInWithPopup, mesmo POST em
/login/callback com o token CSRF. O que diverge é moldura: o bloco de marca (3 têm, 2 não) e o
título, em quatro formatos (Login, Login — …, Login | …, Login - …).
Ou seja: está duplicado exatamente o que não deveria — o fluxo de autenticação, onde mora o risco — e o que varia é cosmético. Um bug de auth custa 5 PRs em 5 repos, cada um com uma cópia que já divergiu.
Decisão¶
A lib é dona da tela inteira; o app parametriza. A login.jte sobe para o xadm-seguranca e sai
dos 5 repos. O app não escreve view — configura:
xadm:
views:
app-nome: "Integração X-Adm Webstorm" # já existente: opt-in do XadmViewModel
login:
marca: true # default false
tagline: "Um ERP Completo para sua empresa"
O título é derivado do app-nome ("Login — ${app-nome}"), o que mata os quatro formatos por
construção em vez de pedir que cinco apps escrevam a mesma string do mesmo jeito. Property ausente
degrada para o default e nunca falha o boot — são cosméticas, diferente de property de segredo/auth,
onde a casa é fail-closed.
A view da lib é AUTOCONTIDA — não chama o kit.layout (emenda de 2026-09-08, abaixo).
A view de lib mora sob namespace (xadm/login.jte), e isso virou norma para qualquer view
empacotada em lib — ver 0025 (emenda de 2026-09-08) e
Micronaut §UI. Não é estilo: o JTE
transforma subdiretório em pacote, então uma view na raiz do jar geraria a mesma FQCN que a do app,
uma sombreando a outra sem erro nem aviso.
Adoção é migração, no mesmo PR (§5): sobe a lib, apaga a view local e o ViewModelProcessor de
appVersao, põe as properties.
Consequências¶
- Um bug de auth vira um bump de lib, não 5 PRs — que é o ponto.
- A identidade para de divergir na porta do app: a tela que alguém de fora vê primeiro passa a ser a mesma em toda a casa.
- O
appVersaopassa a vir da lib. Okit/headerRight.jtejá o renderiza sob@if(appVersao != null); a lib já tem o valor (VersaoInfo). Some o processor per-app. - Acoplamento novo, que nenhum gate pega: o markup vem do jar da lib e o CSS que o estiliza
(
.xadm-login-logo,.xadm-login-tagline) vem docustom-theme.css, template rastreado do central. Dois repos, dois versionamentos. App com tema velho + lib nova renderiza a marca sem estilo, e só o olho pega — por isso o CHANGELOG do módulo declara a versão mínima da constituição, e o sintoma está nomeado na receita. - O JTE entra no
xadm-segurancacomocompileOnly— VERIFICADO no artefato (2026-09-08). Preserva a decisão do módulo ("o runtime de views é provido pelo app consumidor"): app API-only que usa a lib de auth não ganha motor de view. O jar traz aJteloginGeneratedsob namespace e oreflection-config.jsongerado; o POM não tem nenhuma entradagg.jte. Os planos B — cair paraimplementation, ou criar o móduloxadm-seguranca-views— saem da mesa. Mas há uma condição, e ela não é opcional: o POM só fica limpo porque oimplementation.extendsFrom(jteGenerate)do plugin foi desfeito no módulo. Sem isso, a extensão de build (jte-native-resources+ ojte-runtimeque ela arrasta) sai como dependência de runtime de todo adotante e ocompileOnlyvira letra morta — em silêncio, porque só o POM publicado revela. Receita e cura em Micronaut §Native. - View órfã não quebra o build (a da lib está sob namespace) — fica arquivo morto, cobrável pelo
§3b da
/xadm-docs.
Correção 2026-09-08 — a view da lib é autocontida, não usa o kit.layout¶
O que motivou: a decisão original dizia que a view usaria o kit.layout, como as telas dos apps.
Isso não compila. Verificado no artefato gerado: @template.kit.layout(...) é resolvido em tempo
de geração, não em runtime — o JteloginGenerated.java:9 de um app real contém a chamada Java direta
gg.jte.generated.precompiled.kit.JtelayoutGenerated.render(...). Logo, uma view empacotada na lib que
chamasse o kit exigiria kit/layout.jte dentro do build da lib — e o kit é template rastreado,
copiado para os apps.
A saída adotada: a xadm/login.jte traz o próprio documento mínimo — logo, nome do app e
versão —, usando o custom-theme.css e o Bootstrap vendorizado que o app já serve. A identidade
continua vindo de onde sempre veio: o CSS, não a estrutura do layout. E a tela não perde nada do
que o kit tem de rico, porque sem sessão nada disso existe: não há menu (todo item redirecionaria
de volta) nem chip de usuário.
Custo aceito: ~10 linhas de header vivem duplicadas no jar e podem divergir visualmente do header logado com o tempo. É o preço de não ter dependência de template entre repos.
Alternativas descartadas nesta emenda:
- Lib carrega o kit sob namespace próprio (
xadm/kit/layout.jte): compila e não colide, mas cria duas cópias do layout — o login renderiza a do jar, o resto do app a dele. Quando o kit muda no central (como acabou de mudar, CDN→asset local), o app re-deriva e a lib só na release seguinte: a tela de login carregaria o Bootstrap do CDN enquanto o resto carrega local, ou pediria um asset que o app não copiou. Skew declarável, não eliminável. - Mover o kit para a lib, com os apps chamando
@template.xadm.kit.layout: um dono só, o mais limpo em tese. Custo em dois eixos — 98 views em 6 apps mudam a chamada, e correção no kit deixa de chegar por/xadm-docsre-derive, passando a exigir release da lib e bump em cada app; mais lento que hoje. - Kit como artefato de template publicado, consumido no build por lib e apps: manteria um dono sem migrar as 98 views, ao custo de um mecanismo de distribuição de template JTE que a casa não tem — infra nova para um problema que a saída adotada resolve sem nenhuma.
Alternativas descartadas¶
- Template rastreado no kit (
templates/views-jte/kit/login.jte, re-derivado pela/xadm-docs): é a mecânica que a casa já usa e entende, e mataria o drift de conteúdo. Mas a view continuaria copiada em 5 repos: um fix de auth exigiria os 5 re-derivarem e re-releasarem, e a divergência voltaria em quem editasse a cópia. O que se quer eliminar é a cópia, não só o drift. - Lib dona só do mecanismo (um fragmento/JS com Firebase + CSRF + callback, markup no app): preservaria liberdade total de layout. Mas continuariam 5 arquivos vivos, e o drift voltaria pelo markup — que é exatamente onde os 5 já divergiram.
- Bean customizador (
LoginPageCustomizerimplementado pelo app) em vez de properties: mais poder, mas vira API pública da lib (quebrar depois é breaking) e exige código onde bastaria uma linha de YAML. - Slot JTE para o app injetar markup próprio: é a armadilha do kit em 2026-07 — slot especulativo, zero uso, que derrubou 126 views (0015). A variação real observada cabe nas properties.
- Módulo novo
xadm-seguranca-views: fronteira de dependência mais limpa que ocompileOnly, mas seria o nono módulo doxadm-commons— a constituição §5 nomeia oito — e separaria o controller da view dele.
Referências¶
- Receita e properties: Micronaut §UI
- Norma do namespace: 0025 (emenda) · constituição §5
- 0019 (os módulos e suas capacidades) ·
0010 (UI unificada) ·
0017 (a fronteira
custom-theme.css×app.css)