No artigo anterior, expliquei por que Django Signals costumam ser usados além do que deveriam e como isso acaba escondendo regras de negócio importantes.
Agora vem a pergunta natural:
Se eu não devo usar signals para regras de negócio, o que usar no lugar?
A resposta mais comum em projetos Django maduros é: services.
Neste artigo, vou mostrar como refatorar signals para services, quando fazer essa mudança e como organizar isso de forma limpa, sem reinventar o framework.
O problema prático que estamos resolvendo
Vamos partir de um cenário real (e muito comum):
-
Um
post_saveem um model -
Que executa lógica de negócio
-
Criando ou alterando outros objetos
-
Disparando efeitos colaterais importantes
Exemplo clássico (simplificado):
@receiver(post_save, sender=User)
def criar_perfil(sender, instance, created, **kwargs):
if created:
Perfil.objects.create(user=instance)
Funciona? Sim.
Mas esse código cria alguns problemas claros:
-
A criação do perfil é implícita
-
Quem cria o usuário não sabe que isso acontece
-
Testes ficam mais difíceis
-
A regra de negócio está espalhada
Agora vamos resolver isso de forma mais explícita.
O que são Services no contexto de Django?
Em Django, services não são um recurso oficial do framework.
Eles são um padrão arquitetural.
A ideia é simples:
Colocar regras de negócio em funções ou classes explícitas, chamadas intencionalmente.
Normalmente ficam em arquivos como:
-
services.py -
use_cases.py -
domain/services.py
O nome importa menos do que a intenção.
Refatorando um Signal para um Service (passo a passo)
Cenário: criação de usuário com perfil
Antes (com signal)
-
Useré criado -
Um signal cria o perfil automaticamente
-
Quem chama não tem controle
Depois (com service)
Vamos criar um service explícito:
user = User.objects.create_user(**dados)
Perfil.objects.create(user=user)
return user
Agora, no fluxo de cadastro:
username="eduardo",
email="eduardo@email.com",
password="123456"
)
O que mudou?
-
A regra de negócio está visível
-
Não há efeito colateral oculto
-
O código é fácil de testar
-
A leitura fica óbvia
Services deixam o código mais honesto
Uma das maiores vantagens dos services é honestidade do código.
Quando você lê:
finalizar_ordem(ordem)
Você espera que:
-
Algo importante aconteça
-
Vários passos ocorram
-
Regras sejam aplicadas
Quando você lê:
ordem.save()
Você não espera:
-
Movimentação financeira
-
Atualização de estoque
-
Envio de notificações
-
Geração de relatórios
Services alinham expectativa com realidade.
Refatorando um caso mais crítico: regras financeiras
Vamos para um exemplo mais sensível.
def gerar_conta_financeira(sender, instance, **kwargs):
if instance.status == "concluida":
ContaFinanceira.objects.create(...)
Problemas:
-
Executa em todo
save() -
Pode gerar duplicidade
-
Depende de estado anterior
-
Difícil de testar
if ordem.status == "concluida":
return
ordem.save()
ordem=ordem,
valor=ordem.valor_total
)
Agora:
-
A regra está centralizada
-
Não depende de signal
-
O fluxo é claro
-
O impacto financeiro é explícito
E onde entram os Signals, então?
Depois da refatoração, os signals não desaparecem — eles ficam no lugar certo.
Bons usos após a refatoração:
-
Auditoria
-
Logs
-
Cache
-
Integração externa
Exemplo saudável:
def registrar_log(sender, instance, **kwargs):
registrar_evento("Ordem salva", instance.id)
Note a diferença:
-
O sistema não depende disso para funcionar
-
É apenas um efeito colateral técnico
Como organizar services em projetos maiores
Estrutura comum e funcional:
├── models.py
├── views.py
├── services.py
├── selectors.py
└── tests/
Separação clara:
-
models → estrutura de dados
-
services → regras de negócio
-
views → entrada/saída (HTTP)
-
selectors → consultas complexas
Isso escala muito melhor do que signals espalhados.
Refatorar tudo de uma vez? Não.
Uma armadilha comum é tentar:
“Vamos remover todos os signals do projeto agora.”
Isso quase sempre dá errado.
Abordagem segura:
-
Identifique signals com regra de negócio
-
Refatore os mais críticos primeiro
-
Crie services claros
-
Migre o uso gradualmente
-
Deixe signals apenas para efeitos técnicos
Regra prática final
Se você está em dúvida entre signal ou service, use esta regra:
Se alguém precisar chamar isso conscientemente, é um service.
Se alguém não precisar saber que isso acontece, pode ser um signal.
Na maioria dos casos importantes, a resposta será service.
Conclusão
Refatorar Django Signals para Services não é modismo.
É um passo natural de maturidade arquitetural.
O resultado é:
-
Código mais legível
-
Menos efeitos colaterais invisíveis
-
Testes mais simples
-
Menos bugs difíceis de rastrear
Signals continuam existindo — mas deixam de ser protagonistas e passam a ser coadjuvantes técnicos.
E, em projetos reais, isso faz toda a diferença.