en
· 4 min de leitura

Onde as regras de negócio deveriam morar, e onde elas acabam

A regra é escrita uma vez na especificação e cinco vezes no código. Como ela se espalha, e como trazê-la de volta para casa.

architectureengineering

Pergunte a um time onde mora a regra 'um pedido acima de determinado valor precisa de aprovação do gerente' e observe. O backend aponta para um serviço. O frontend aponta para um validador de formulário. A pessoa de dados aponta para uma view SQL. Operações aponta para um cron que envia o e-mail de aprovação. Todos estão certos, e esse é o problema.

Regras de negócio são o código mais valioso de um sistema e o mais propenso a se espalhar. Todo o resto, o encanamento, a cola do framework, a infraestrutura, é substituível. As regras são o motivo de o software existir. E na maioria das bases que audito elas moram em pelo menos quatro lugares que discordam entre si.

Onde elas acabam

O espalhamento segue um padrão. Uma regra nasce na camada de domínio, ou onde o time guarde a lógica central. Depois o frontend precisa mostrar um erro antes do envio, então a regra é copiada para um validador. Depois um relatório precisa contar aprovações, então a regra vira SQL. Depois uma notificação precisa disparar, então um job agendado deriva de novo quem precisa de aprovação. Depois um script de importação precisa ignorá-la para dados legados, então ganha uma flag.

Cinco cópias. Cada uma escrita por uma pessoa diferente sob um prazo diferente. Quando o limite muda, três cópias são atualizadas, uma é esquecida, e a quinta está num script de que ninguém lembra.

O pior lugar em que encontro regras é dentro do banco, em triggers e stored procedures, invisíveis para quem lê o código da aplicação. O segundo pior é o frontend, porque é a única cópia que o usuário experimenta, e ela define o produto em silêncio, independente do que o backend pense.

Onde elas deveriam morar

Uma regra deveria ter exatamente uma casa oficial, e essa casa deveria ser código simples sem framework: uma função ou classe pequena que recebe valores do domínio e devolve uma decisão. Sem HTTP, sem ORM, sem biblioteca de data que esconda a hora atual. Testável com um teste unitário de milissegundos.

Todo o resto consulta essa casa. O handler da API chama a regra antes de persistir. O frontend chama um endpoint que a executa ou, se latência importa, importa o mesmo código como pacote gerado da mesma fonte. O relatório não codifica a regra; lê a decisão que a regra já registrou, uma coluna como requer_aprovacao escrita quando o pedido foi feito.

Esse último ponto importa mais do que parece. Registrar decisões, não só dados, é como você impede que regras sejam derivadas de novo. Se a linha do pedido diz que ele precisou de aprovação, o relatório, o job de notificação e o log de auditoria leem isso. Ninguém recalcula, então ninguém erra quando o limite muda e pedidos antigos devem manter o resultado antigo.

As checagens que revelam o espalhamento

Quando audito isso, uso três checagens.

  • Procure a constante. Escolha um número de negócio, um limite, uma taxa, um teto, e busque no repositório inteiro, incluindo SQL e frontend. Toda ocorrência fora de um único arquivo é uma cópia.
  • Mude a regra no papel e trace o diff. Se a mudança toca mais de um módulo e um arquivo de teste, a regra escapou.
  • Peça o histórico de uma decisão. Se ninguém consegue dizer por que um pedido específico foi aprovado em março sem rodar a lógica de hoje, as decisões estão sendo derivadas de novo em vez de registradas.

Como as regras escapam, e como impedir

Regras escapam por bons motivos. O frontend quer feedback instantâneo. O relatório quer rodar sem chamar uma API. O job em lote roda de madrugada, quando o serviço pode estar fora. Cada cópia resolve um problema real, então dizer 'não duplique' não funciona.

O que funciona é dar a cada necessidade um caminho sancionado. Para feedback instantâneo: distribua a regra como um pacote puro que o frontend pode importar. Para relatórios: registre decisões na escrita. Para jobs em lote: faça-os chamar o serviço, e torne o serviço confiável o bastante para isso, um problema à parte que você deveria resolver de qualquer jeito.

E dê nome à casa. Em toda base que monto existe um diretório que é visivelmente o domínio, com um README curto: regras moram aqui e em nenhum outro lugar. Isso não impede o espalhamento sozinho. Dá ao revisor que encontra um limite numa view SQL um lugar para apontar.

Sistemas em que as regras têm uma casa são aqueles em que uma mudança de produto é um pull request de uma linha. Sistemas com regras espalhadas tratam cada mudança como um projeto de arqueologia. A diferença não é o framework nem a linguagem. É se alguém decidiu, cedo, onde a verdade mora.