Class RegrasArquitetura
ArchitectureTest com @AnalyzeClasses(packagesOf = Application.class) e compõe
estas regras (parametrizadas pelo pacote-base) com as regras próprias da sua topologia
— a fábrica só traz o que é comum a todo app, sem achatar a topologia de cada um.
Uso:
@ArchTest static final ArchRule importNaoVazio = RegrasArquitetura.importNaoVazio();
@ArchTest static final ArchRule semCiclos =
RegrasArquitetura.semCiclosEntreFatias("br.com.xadm.meuapp");
@ArchTest static final ArchRule controllerSemRepository =
RegrasArquitetura.controllerNaoAcessaRepository();
@ArchTest static final ArchRule nadaDependeDeController =
RegrasArquitetura.nadaDependeDeController();
Comece pelo importNaoVazio(). As demais regras desta fábrica só
valem alguma coisa se o import trouxe classe; sem essa guarda no mesmo ArchitectureTest,
herdar as regras é herdar um atestado do nada.
-
Method Summary
Modifier and TypeMethodDescriptionstatic com.tngtech.archunit.lang.ArchRulebaseNaoDependeDeFatias(String pacoteBase, String motivo, String... pacotesFatia) A base transversal não depende de nenhuma das fatias — a forma que o package-by-feature realmente pede.static com.tngtech.archunit.lang.ArchRulecamadaNaoDepende(String pacoteOrigem, String pacoteDestino, String motivo) Uma camada não depende de outra (ex.: parsing não depende da camada HTTP).static com.tngtech.archunit.lang.ArchRuleController não acessa a persistência direto: repository ejavax.sqlficam atrás de um service da feature.static com.tngtech.archunit.lang.ArchRuleO import do ArchUnit trouxe pelo menos uma classe — a defesa anti-vácuo, que prova que as outras regras rodaram sobre algo.static voidimportNaoVazio(com.tngtech.archunit.core.domain.JavaClasses classes) Mesma defesa anti-vácuo doimportNaoVazio(), na forma de método — para quem prefere o@ArchTest static void(o ArchUnit injeta o parâmetroJavaClasses):static com.tngtech.archunit.lang.ArchRuleNada depende de controller, nem dos DTOs HTTP aninhados nele: o service devolve entidade ou record próprio, e o controller mapeia para o DTO.static com.tngtech.archunit.lang.ArchRulesemCiclosEntreFatias(String pacoteBase) Sem ciclos entre as fatias de topo do pacote-base — invariante que vale tanto em package-by-layer quanto em package-by-feature.
-
Method Details
-
importNaoVazio
public static com.tngtech.archunit.lang.ArchRule importNaoVazio()O import do ArchUnit trouxe pelo menos uma classe — a defesa anti-vácuo, que prova que as outras regras rodaram sobre algo.Import zerado não é erro no ArchUnit: é descarte silencioso, e toda regra de fronteira passa vácua, verde, testando nada. As duas causas conhecidas:
- ASM abaixo do piso do JDK — classe num
class file majorque o ASM bundleado não conhece é pulada no import (Java 25 = major 69 exige ASM ≥ 9.8 / ArchUnit ≥ 1.4.1); @AnalyzeClassesapontando pacote inexistente — tipicamente um rename de pacote que deixou o string-literal para trás.
Sem esta regra as duas falham para cima: verde. Com ela, falham alto.
- Returns:
- regra ArchUnit que exige ao menos uma classe importada
- ASM abaixo do piso do JDK — classe num
-
importNaoVazio
public static void importNaoVazio(com.tngtech.archunit.core.domain.JavaClasses classes) Mesma defesa anti-vácuo doimportNaoVazio(), na forma de método — para quem prefere o@ArchTest static void(o ArchUnit injeta o parâmetroJavaClasses):@ArchTest static void importNaoVazio(JavaClasses classes) { RegrasArquitetura.importNaoVazio(classes); }- Parameters:
classes- classes importadas pelo@AnalyzeClasses, injetadas pelo ArchUnit- Throws:
AssertionError- se o import veio vazio
-
semCiclosEntreFatias
Sem ciclos entre as fatias de topo do pacote-base — invariante que vale tanto em package-by-layer quanto em package-by-feature.- Parameters:
pacoteBase- pacote-base do app (ex.:br.com.xadm.meuapp)- Returns:
- regra ArchUnit de ausência de ciclos entre as fatias de
{pacoteBase}.(*)..
-
camadaNaoDepende
public static com.tngtech.archunit.lang.ArchRule camadaNaoDepende(String pacoteOrigem, String pacoteDestino, String motivo) Uma camada não depende de outra (ex.: parsing não depende da camada HTTP).Escreva os pacotes com o nome completo do app (
br.com.xadm.meuapp.api..): no destino, o padrão curto..seguranca..casa também o pacote da lib (br.com.xadm.comum.seguranca). Origem que não casa nenhuma classe importada falha, em vez de passar vácua — é o literal que ficou para trás num rename de pacote.Para proibir vários destinos de uma vez (o caso do package-by-feature), use
baseNaoDependeDeFatias(String, String, String...)— que rende uma regra, não N.- Parameters:
pacoteOrigem- padrão do pacote de origem (ex.:br.com.xadm.meuapp.processing..)pacoteDestino- padrão do pacote proibido como dependência (ex.:br.com.xadm.meuapp.api..)motivo- justificativa (aparece na falha)- Returns:
- regra ArchUnit proibindo a dependência origem→destino
-
baseNaoDependeDeFatias
public static com.tngtech.archunit.lang.ArchRule baseNaoDependeDeFatias(String pacoteBase, String motivo, String... pacotesFatia) A base transversal não depende de nenhuma das fatias — a forma que o package-by-feature realmente pede.É
camadaNaoDepende(String, String, String)com N destinos numa regra só: o app com sete features escreveria sete regras, sete nomes e colheria sete falhas para o mesmo defeito. Aqui é uma:@ArchTest static final ArchRule comumNaoDependeDeFatias = RegrasArquitetura.baseNaoDependeDeFatias( "br.com.xadm.meuapp.comum..", "base transversal não conhece feature", "br.com.xadm.meuapp.admin..", "br.com.xadm.meuapp.login..", "br.com.xadm.meuapp.deploy..");Os pacotes vão com o nome completo e a base vazia falha, como na
camadaNaoDepende(String, String, String).- Parameters:
pacoteBase- padrão do pacote da base transversal (ex.:br.com.xadm.meuapp.comum..)motivo- justificativa (aparece na falha)pacotesFatia- padrões dos pacotes de feature proibidos; ao menos um- Returns:
- regra ArchUnit proibindo a base de depender de qualquer uma das fatias
- Throws:
IllegalArgumentException- se nenhuma fatia for informada — regra sem destino passaria vácua, que é o mesmo falso atestado que oimportNaoVazio()combate
-
controllerNaoAcessaRepository
public static com.tngtech.archunit.lang.ArchRule controllerNaoAcessaRepository()Controller não acessa a persistência direto: repository ejavax.sqlficam atrás de um service da feature. No package-by-feature controller e repository moram no mesmo pacote, então a fronteira é pelo tipo — acamadaNaoDepende(String, String, String)não os separa.Os tipos do Micronaut entram pelo nome: o app sem micronaut-data usa a regra sem a classe no classpath. Sem nenhum controller Micronaut no import a regra falha, em vez de passar vácua.
- Returns:
- regra ArchUnit proibindo controller Micronaut de depender de um
GenericRepositoryou dejavax.sql
-
nadaDependeDeController
public static com.tngtech.archunit.lang.ArchRule nadaDependeDeController()Nada depende de controller, nem dos DTOs HTTP aninhados nele: o service devolve entidade ou record próprio, e o controller mapeia para o DTO. Vale para domínio, parsing e infra.O código que o Micronaut gera em build fica fora da origem: bean definitions e introspecções (
$Nome$…) e os serdes que o micronaut-serde gera para o DTO aninhado, que dependem do controller por construção e não levam$no nome.- Returns:
- regra ArchUnit proibindo classe de fora do controller de depender dele ou dos tipos aninhados nele
-