Class RegrasArquitetura

java.lang.Object
br.com.xadm.comum.teste.RegrasArquitetura

public final class RegrasArquitetura extends Object
Fábrica das regras ArchUnit compartilhadas da casa. Cada app monta seu 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 Type
    Method
    Description
    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.
    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).
    static com.tngtech.archunit.lang.ArchRule
    Controller não acessa a persistência direto: repository e javax.sql ficam atrás de um service da feature.
    static com.tngtech.archunit.lang.ArchRule
    O import do ArchUnit trouxe pelo menos uma classe — a defesa anti-vácuo, que prova que as outras regras rodaram sobre algo.
    static void
    importNaoVazio(com.tngtech.archunit.core.domain.JavaClasses classes)
    Mesma defesa anti-vácuo do importNaoVazio(), na forma de método — para quem prefere o @ArchTest static void (o ArchUnit injeta o parâmetro JavaClasses):
    static com.tngtech.archunit.lang.ArchRule
    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.
    static com.tngtech.archunit.lang.ArchRule
    Sem ciclos entre as fatias de topo do pacote-base — invariante que vale tanto em package-by-layer quanto em package-by-feature.

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • 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 major que o ASM bundleado não conhece é pulada no import (Java 25 = major 69 exige ASM ≥ 9.8 / ArchUnit ≥ 1.4.1);
      • @AnalyzeClasses apontando 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
    • importNaoVazio

      public static void importNaoVazio(com.tngtech.archunit.core.domain.JavaClasses classes)
      Mesma defesa anti-vácuo do importNaoVazio(), na forma de método — para quem prefere o @ArchTest static void (o ArchUnit injeta o parâmetro JavaClasses):
      @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

      public static com.tngtech.archunit.lang.ArchRule semCiclosEntreFatias(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.
      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 o importNaoVazio() combate
    • controllerNaoAcessaRepository

      public static com.tngtech.archunit.lang.ArchRule controllerNaoAcessaRepository()
      Controller não acessa a persistência direto: repository e javax.sql ficam atrás de um service da feature. No package-by-feature controller e repository moram no mesmo pacote, então a fronteira é pelo tipo — a camadaNaoDepende(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 GenericRepository ou de javax.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