Os exemplos de código virão em uma revisão posterior.
Nesta página
Propriedades de arquitetura tratadas
- P-1Uma hierarquia selada que separa propostas de valores verificados, validação na fronteira web e regras de módulo que o build verifica.
- P-4Micrometer Observation e Tracing para o contexto de execução, a identidade de auditoria em todo contrato e entradas de auditoria escritas dentro da transação de negócio.
- P-5Procedência como campos dos tipos de valor, serializada explicitamente.
- P-6Verificação de arquitetura na fase de testes, e checks obrigatórios que não podem passar por terem sido pulados.
- P-7Portas no módulo de domínio e adaptadores em módulos próprios.
- P-8Uma interface de política com um adaptador para o motor, e decisões registradas com sua revisão.
- P-2, P-3Apenas na profundidade que o COADF publica.
Intenção de arquitetura
A força do Spring para essas propriedades é a declaração: restrições sobre tipos, transações e validação aplicadas pelo contêiner, observações configuradas uma única vez, regras de módulo checadas por um teste. Sua falha característica é o outro lado do mesmo design. Uma declaração vale nos caminhos que o contêiner intercepta, por meio de um proxy, em um binding web ou em um executor que ele configurou, e em nenhum outro lugar. Uma propriedade escrita como anotação só é tão forte quanto o conjunto de chamadas que de fato passam pelo Spring.
Este perfil mapeia cada propriedade para o Spring Boot 4 e o Spring Framework 7, a geração atual quando foi escrito, e nomeia os pontos em que uma propriedade declarada deixa de valer em silêncio.
Correspondência tecnológica
| Propriedade de arquitetura | Java e Spring |
|---|---|
| Tipo de fronteira | Uma interface sealed com implementações record: um tipo de proposta e um tipo verificado, construídos em módulos diferentes. Records são portadores de dados com imutabilidade rasa: um componente que é uma lista mutável continua mutável, por isso ele deve ser copiado no construtor. Classes seladas são um recurso padrão desde o Java 17. |
| Validação em tempo de execução | Restrições do Jakarta Validation nos tipos de requisição, disparadas com @Valid. Uma validação de @RequestBody que falha lança MethodArgumentNotValidException, respondida por padrão com 400. |
| Campos desconhecidos | Uma decisão explícita e testada, nos tipos de fronteira, sobre propriedades que o tipo não declara, em vez do que quer que a configuração do mapeador JSON seja no momento. |
| Regra de arquitetura | O ApplicationModules.verify() do Spring Modulith, ou regras do ArchUnit escritas como testes unitários. |
| Contexto de execução | Micrometer Observation e Micrometer Tracing, com ponte para o OpenTelemetry. |
| Contexto assíncrono | ContextPropagatingTaskDecorator com a biblioteca context-propagation do Micrometer; para o executor autoconfigurado, a propriedade do Boot spring.task.execution.propagate-context. |
| Trilha de auditoria | Entradas escritas na transação de negócio; grants e triggers do banco de dados como no exemplo de PostgreSQL; o registro de publicação de eventos do Spring Modulith para eventos que não podem se perder. |
| Isolamento de normas | Interfaces de porta no módulo de domínio; um módulo adaptador por sistema externo; o cliente do fornecedor nunca visível para o domínio. |
| Política | Uma interface de política pertencente ao domínio, com um adaptador para o Open Policy Agent, ou tabelas de regras; a revisão armazenada na decisão. |
| Testes de integração | Um PostgreSQL real via Testcontainers, conectado com @ServiceConnection. |
Padrão de referência
Os exemplos de código virão em uma revisão posterior. Eles ficam adiados até que uma pessoa revisora que trabalhe neste ecossistema os tenha lido, em vez de serem escritos para que os quatro perfis pareçam iguais. A estrutura abaixo é o padrão de referência que esses exemplos vão implementar.
Módulos
inferencechama o modelo e devolve umProposal, um record que carrega o valor, o método e a procedência. Ele não tem dependência derecordsnem deaudit, e uma verificação do Modulith ou uma regra do ArchUnit afirma isso em um teste.recordsé o único módulo que pode criar umVerifiedValue. O tipo não é público fora do módulo, ou é uma classe cujo construtor não é público: um record público não pode esconder seu construtor, porque o construtor canônico de um record público precisa ser, ele próprio, público. A criação passa por uma factory que exige a verificação de uma pessoa nomeada.auditacrescenta entradas com os campos publicados, dentro da transação da mudança que registra, por meio de um repositório que não oferece atualização nem exclusão.classificationdeclara a porta;classification.vendora implementa, e é o único módulo que pode ver o cliente do fornecedor ou seus tipos.policydeclara a interface de política e o registro de decisão; um módulo adaptador conversa com o motor.
Fronteiras em tempo de execução
A validação acontece no binding web, onde o Spring avalia as restrições sobre o corpo da requisição, e de novo dentro da factory do domínio, porque o binding web é um ponto de entrada entre vários: um listener de mensagens, um job em lote e um teste chegam todos ao domínio sem passar por ele.
As observações envolvem a fronteira e as chamadas externas. Todo executor que roda trabalho para uma requisição é configurado para propagar o contexto, e a identidade de auditoria é um campo de toda mensagem e de todo argumento de job, nunca recuperada de uma thread-local depois do fato.
Modos de falha
A autoinvocação contorna o proxy
O Spring AOP é baseado em proxy: uma chamada de um método de um bean para outro método do mesmo bean não passa pelo proxy, então a validação de métodos e o
@Transactionalnão se aplicam a ela. Uma restrição que vale para quem chama de fora do bean não vale para o próprio bean, que muitas vezes é onde começa o caminho em lote.Restrições declaradas, nunca avaliadas
Restrições do Jakarta Validation em um tipo não fazem nada por si mesmas. Elas são avaliadas onde algo dispara a validação:
@Validou@Validatedem um binding web, ou a validação de métodos por meio do proxy. Um listener de mensagens que recebe o mesmo tipo não valida nada, a menos que peça.Propriedades não declaradas deixadas à configuração do mapeador
Se um documento JSON recebido pode carregar propriedades que o tipo não declara é uma configuração do mapeador JSON. Para um tipo de fronteira, a resposta certa costuma ser recusá-las, e essa decisão pertence ao tipo e a um teste, não a uma propriedade da aplicação que outra pessoa pode mudar.
Contexto perdido em
@Asynce em executores própriosPropagar o contexto de observação para outras threads exige um
ContextPropagatingTaskDecoratore a biblioteca context-propagation; para o executor autoconfigurado, o Spring Boot exige ativação explícita por meio despring.task.execution.propagate-context, que vem desligada por padrão. O trabalho entregue a um pool de threads roda então sem seu trace.Imutabilidade do ORM tomada por salvaguarda de auditoria
O Hibernate ignora mudanças em memória em uma entidade
@Immutablegerenciada, sem update e sem exceção. Atualizações em massa contra ela lançam exceção por padrão no Hibernate 7 e apenas geravam um aviso no 6.6. Nada disso impede que um segundo cliente, um script ou uma migração atualize a tabela; a proteção pertence ao banco de dados.Eventos de auditoria perdidos depois do commit
Um evento tratado depois do commit da transação se perde se o handler falhar, a menos que algo o tenha registrado. O registro de publicação de eventos do Spring Modulith escreve uma entrada na transação que publica e mantém as publicações que falharam para reenvio; republicá-las automaticamente ao reiniciar é uma configuração que precisa ser ligada.
Um record público como tipo verificado
O construtor canônico de um record público precisa ser público, então qualquer código que enxergue o tipo pode construir um valor verificado sem uma verificação. O tipo deve ficar fora de alcance, ou ser uma classe com construtor privado e uma factory.
Duas pontes de tracing
O Micrometer Tracing faz ponte para o Brave ou para o OpenTelemetry, e sua documentação pede que se escolha apenas um. Dois no classpath produzem traces que dependem de qual deles prevalece.
Testes de integração contra outro banco de dados
Grants, triggers e o comportamento das restrições são propriedades do banco de dados real. Testes contra um substituto embarcado verificam o código, e não a propriedade.
Verificação
Teste unitário
Passa quando: O tipo verificado só pode ser criado pela factory, e só com a verificação de uma pessoa nomeada; uma rejeição deixa o atributo vazio.
Prova de dentes: Tornar o tipo verificado público em uma branch descartável: um teste que afirma que ele não é acessível a partir do módulo de inferência falha.
Teste de arquitetura
Passa quando: A verificação do Spring Modulith passa, ou passam as regras do ArchUnit: nenhuma dependência de
inferencepararecordsouaudit, nenhum tipo de fornecedor no domínio. O ArchUnit checa dependências entre pacotes e classes como testes unitários comuns, e a verificação do Modulith rejeita ciclos entre módulos e referências a pacotes internos de outro módulo, exceto a módulos declarados abertos. Qualquer das duas ferramentas checa as regras que foram escritas; se são as regras certas é uma questão do design da equipe.Prova de dentes: Plantar a dependência proibida: a verificação falha.
Teste de integração
Passa quando: Contra um PostgreSQL do Testcontainers: o papel da aplicação não consegue atualizar nem apagar a trilha, e as entradas são escritas na mesma transação que a mudança.
Prova de dentes: Fazer rollback da transação de negócio depois de escrever: a entrada de auditoria não pode sobreviver a ele.
Teste de integração
Passa quando: O contexto sobrevive aos executores que a aplicação usa: um exportador em memória mostra o trabalho assíncrono como parte do mesmo trace, e a identidade de auditoria está presente no seu registro.
Prova de dentes: Remover o task decorator: o teste falha.
Teste de contrato
Passa quando: Produtor e consumidor concordam sobre os contratos de proposta e de decisão, com a procedência e a revisão como campos obrigatórios.
Teste de ponta a ponta
Passa quando: Uma saída cujo atributo não foi verificado não pode ser publicada, seja qual for o ponto de entrada por onde a requisição chegou.
Realizações alternativas
- Outros frameworks Java (Quarkus, Micronaut, Jakarta EE) expressam a mesma fronteira com modelos de interceptação diferentes; a questão da autoinvocação precisa ser colocada para cada um.
- SQL explícito (jOOQ ou JDBC puro) para a trilha de auditoria em vez de um ORM, onde as instruções exatas importam mais do que a conveniência do mapeamento.
- Frameworks de event sourcing onde o histórico é o modelo.
- Tabelas de regras mantidas pela aplicação em vez de um motor de políticas externo, com a mesma exigência quanto à revisão da política.
Escolhas de compromisso
- O declarativo é conciso e invisível. Quem lê não consegue ver, no ponto de chamada, se uma restrição ou uma transação se aplica; os testes precisam mostrar isso.
- O Spring Modulith codifica convenções, o que é rápido de adotar e opinativo; o ArchUnit é genérico e exige que as regras sejam escritas por extenso.
- O JPA é conveniente e põe uma camada entre o código e as proteções do próprio banco de dados. Para a trilha de auditoria, essa camada é a parte através da qual é preciso enxergar.
- Fronteiras de módulo dentro de um único artefato implantável mantêm simples o modelo operacional, e fazem a fronteira depender inteiramente da verificação do build.
Limitações
- Nenhum código é publicado para este perfil no Companion 1.0, e nenhum exemplo foi executado. O comportamento descrito foi tirado da documentação oficial atual, conferida em 11 de setembro de 2026, do Spring Boot 4.1, Spring Framework 7.0, Spring Modulith 2.1, Hibernate ORM 7.4 e Java SE 25.
- Nada aqui mostra como a confiança é representada, quando a revisão é exigida ou como a revisão é organizada; o COADF não publica essas partes de P-2 e P-3.
- Tudo o que é citado é comportamento de linguagem ou de framework. A propriedade de arquitetura, a de que uma proposta não pode se tornar um registro sem uma verificação, é um design que a equipe ainda precisa fazer e testar: nenhuma anotação, record ou regra de módulo a fornece por si só.
O que este perfil não estabelece
Seguir este perfil não estabelece conformidade regulatória, certificação nem avaliação da conformidade, e o Spring não é exigido pelo COADF.
Ambiente de referência testado
Nenhum exemplo foi executado para este perfil.
Fontes
- Oracle: java.lang.Record · documentação da linguagem · Java SE 25 · Verificado em 2026-09-11
- OpenJDK: JEP 409: Sealed Classes · especificação · Verificado em 2026-09-11
- Oracle: Java Language Specification: canonical constructors of record classes · especificação · Java SE 25 · Verificado em 2026-09-11
- Spring Framework: @RequestBody validation · documentação oficial · Spring Framework 7.0 · Verificado em 2026-09-11
- Spring Framework: Understanding AOP proxies · documentação oficial · Spring Framework 7.0 · Verificado em 2026-09-11
- Spring Framework: Spring-driven method validation · documentação oficial · Spring Framework 7.0 · Verificado em 2026-09-11
- Spring Framework: Observability support: context propagation · documentação oficial · Spring Framework 7.0 · Verificado em 2026-09-11
- Spring Boot: Observability: context propagation · documentação oficial · Spring Boot 4.1 · Verificado em 2026-09-11
- Spring Boot: Tracing · documentação oficial · Spring Boot 4.1 · Verificado em 2026-09-11
- Spring Boot: Testcontainers service connections · documentação oficial · Spring Boot 4.1 · Verificado em 2026-09-11
- Spring Modulith: Verifying application module structure · documentação oficial · Spring Modulith 2.1 · Verificado em 2026-09-11
- Spring Modulith: Event publication registry · documentação oficial · Spring Modulith 2.1 · Verificado em 2026-09-11
- Hibernate ORM: Immutable entities · documentação oficial · Hibernate ORM 7.4 · Verificado em 2026-09-11
- ArchUnit: ArchUnit user guide · documentação oficial · ArchUnit 1.5 · Verificado em 2026-09-11
- Micrometer: Micrometer Tracing: supported tracers · documentação oficial · Micrometer Tracing 1.7 · Verificado em 2026-09-11
