Pular para o conteúdo principal

archbase-architecture-rules

O módulo archbase-architecture-rules fornece regras de validação arquitetural baseadas em Taikai/ArchUnit para garantir que projetos sigam os padrões do Archbase Framework.

Por que usar?

  • Prevenir violações: Detecta problemas arquiteturais antes do merge
  • Automatizado: Executa como teste unitário no CI/CD
  • Consistência: Garante que toda a equipe siga os mesmos padrões
  • Feedback rápido: Mensagens claras sobre o que está errado

Instalação

<dependency>
<groupId>br.com.archbase</groupId>
<artifactId>archbase-architecture-rules</artifactId>
<version>${archbase.version}</version>
<scope>test</scope>
</dependency>

Uso Básico

API Fluente

@Test
void shouldFollowArchbasePatterns() {
ArchbaseArchitectureRules.forNamespace("com.minhaempresa.meuprojeto")
.withDddRules() // Entidades, repositórios, camadas
.withSpringRules() // Controllers, Services, @Autowired
.withNamingRules() // Convenções de nomes
.withSecurityRules() // @HasPermission obrigatório
.withMultitenancyRules() // TenantPersistenceEntityBase
.check();
}

Profiles Predefinidos

// API REST com multitenancy (mais comum)
ArchbaseRuleProfiles.multitenantRestApi("com.minhaempresa").check();

// API REST sem multitenancy
ArchbaseRuleProfiles.simpleRestApi("com.minhaempresa").check();

// Microservice simples
ArchbaseRuleProfiles.simpleService("com.minhaempresa").check();

// Apenas regras DDD (para bibliotecas)
ArchbaseRuleProfiles.domainOnly("com.minhaempresa").check();

// Todas as regras (modo estrito)
ArchbaseRuleProfiles.strict("com.minhaempresa").check();

// Regras mínimas (projetos em migração)
ArchbaseRuleProfiles.lenient("com.minhaempresa").check();

Estendendo Classe Base

class MeuProjetoArchitectureTest extends ArchbaseArchitectureTest {

@Override
protected String getBasePackage() {
return "com.minhaempresa.meuprojeto";
}

@Override
protected boolean enableSecurityRules() {
return true; // Valida @HasPermission
}

@Override
protected boolean enableMultitenancyRules() {
return true; // Valida TenantPersistenceEntityBase
}

@Override
protected void configureCustomRules(ArchbaseArchitectureRules rules) {
// Regras específicas do projeto
}
}

Regras Disponíveis

Regras DDD

RegraDescrição
Entidades JPADevem estender PersistenceEntityBase ou TenantPersistenceEntityBase
RepositóriosDevem implementar ArchbaseCommonJpaRepository
Sem métodos customizadosRepositórios não devem ter findByX, @Query - usar QueryDSL no adapter
Separação de camadasDomain não depende de Infrastructure
Pacotes corretosEntidades em domain, entity ou persistence
Repositórios sem métodos customizados

Esta regra garante que todas as queries sejam feitas via QueryDSL no adapter:

// ❌ VIOLAÇÃO - método customizado no repositório
public interface ProdutoRepository extends ArchbaseCommonJpaRepository<...> {
Optional<Produto> findBySku(String sku); // Não permitido!

@Query("SELECT p FROM Produto p WHERE p.ativo = true")
List<Produto> findAtivos(); // Não permitido!
}

// ✅ CORRETO - usar QueryDSL no adapter
@Component
public class ProdutoQueryAdapter {
private final JPAQueryFactory queryFactory;

public Optional<Produto> findBySku(String sku) {
QProduto p = QProduto.produto;
return Optional.ofNullable(
queryFactory.selectFrom(p)
.where(p.sku.eq(sku))
.fetchOne()
);
}
}

Regras Spring

RegraDescrição
NomenclaturaControllers terminam com Controller, Services com Service, etc
Sem @Autowired em camposUsar injeção via construtor
Controllers isoladosNão dependem de outros controllers
CamadasControllers → Services → Repositories (não pular camadas)

Regras de Segurança

RegraDescrição
@HasPermissionTodos os endpoints REST devem ter anotação de segurança
Endpoints públicosDevem ser explicitamente marcados com @PermitAll
Exemplo de controller seguro
@RestController
@RequestMapping("/api/v1/produtos")
@HasPermission(action = "VIEW", resource = "PRODUTO")
class ProdutoController {

@PostMapping
@HasPermission(action = "CREATE", resource = "PRODUTO")
ResponseEntity<ProdutoDTO> criar(@Valid @RequestBody ProdutoCreateDTO dto) {
// ...
}
}

Regras de Nomenclatura

RegraDescrição
InterfacesNão devem ter prefixo I (use UserRepository, não IUserRepository)
ClassesNão devem ter sufixo Impl (use nomes descritivos)
DTOsDevem terminar com DTO ou Dto
ExceçõesDevem terminar com Exception
ConstantesDevem usar UPPER_SNAKE_CASE

Regras de Multitenancy

RegraDescrição
Entidades tenant-awareDevem estender TenantPersistenceEntityBase

Regras Customizadas

ArchbaseArchitectureRules.forNamespace("com.minhaempresa")
.withDddRules()
// Regra customizada
.addRule(TaikaiRule.of(
ArchRuleDefinition.classes()
.that().resideInAPackage("..usecase..")
.should().haveSimpleNameEndingWith("UseCase")
))
.check();

Integração com CI/CD

Crie um teste que será executado automaticamente no build:

src/test/java/com/minhaempresa/ArchitectureTest.java
package com.minhaempresa;

import br.com.archbase.architecture.rules.core.ArchbaseRuleProfiles;
import org.junit.jupiter.api.Test;

class ArchitectureTest {

@Test
void shouldFollowArchbasePatterns() {
ArchbaseRuleProfiles.multitenantRestApi("com.minhaempresa").check();
}
}

Se alguma regra for violada, o teste falha e o build é bloqueado.

Boas Práticas

PráticaDescrição
Execute no CIConfigure para rodar em todo PR
Use checkAll(true)Mostra todos os erros de uma vez
Comece com lenient()Para projetos em migração
Adicione gradualmenteHabilite regras conforme o projeto amadurece
Documente exceçõesSe precisar ignorar uma regra, documente o motivo

Exemplo Completo

src/test/java/com/vendax/ArchitectureTest.java
package com.vendax;

import br.com.archbase.architecture.rules.test.ArchbaseArchitectureTest;
import br.com.archbase.architecture.rules.core.ArchbaseArchitectureRules;
import com.enofex.taikai.TaikaiRule;
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;

class VendaxArchitectureTest extends ArchbaseArchitectureTest {

@Override
protected String getBasePackage() {
return "com.vendax";
}

@Override
protected boolean enableSecurityRules() {
return true;
}

@Override
protected boolean enableMultitenancyRules() {
return true;
}

@Override
protected void configureCustomRules(ArchbaseArchitectureRules rules) {
// Regra específica: Adapters devem terminar com "Adapter"
rules.addRule(TaikaiRule.of(
ArchRuleDefinition.classes()
.that().resideInAPackage("..adapter..")
.should().haveSimpleNameEndingWith("Adapter")
));
}
}

Próximos Passos