Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

06 - Serviços de Domínio (Domain Services)

#ddd#domain-service#politicas#servico-de-aplicacao
video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

Serviços de Domínio (Domain Services)

O serviço de domínio é uma operação significativa do negócio que não pertence naturalmente a
nenhuma entidade nem a nenhum objeto de valor, e que é modelada como uma classe do próprio domínio.
Ele existe porque algumas regras envolvem vários agregados ao mesmo tempo, dependem de conceitos
externos ao modelo ou representam uma atividade que o negócio nomeia, como liquidar uma operação ou
calcular a elegibilidade de um cliente. Forçar essas operações dentro de uma entidade produz acoplamento
artificial, e deixá-las em camadas técnicas esconde lógica de negócio em lugares onde o domínio não
a procura. O serviço de domínio é stateless, fala a linguagem ubíqua e opera sobre objetos de
domínio, e é a terceira opção de destino para uma regra, depois da entidade e do objeto de valor.

Quando uma operação não cabe em entidade nem objeto de valor

O primeiro sinal é a operação envolver mais de um agregado. Transferir dinheiro entre duas contas
altera duas raízes de agregado, e colocar o método transferirPara dentro da conta obriga uma conta
a conhecer as regras internas de outra, além de sugerir que a transação pertence a uma das duas,
quando na verdade pertence à operação. O segundo sinal é a operação usar um conceito externo, como
uma política de crédito calculada com dados de um serviço de bureau, que nenhuma entidade deveria
carregar como dependência. O terceiro sinal é a operação ser nomeada pelo negócio como atividade
própria, sem dono óbvio: conciliar pagamentos, recalcular juros de um contrato, redistribuir estoque.
Nesses casos, uma classe com nome de negócio, operando sobre entidades passadas como parâmetro, é a
modelagem mais honesta. A regra de decisão simples é: se a regra cabe em uma entidade ou em um
objeto de valor, ela mora lá; se não cabe, vira serviço de domínio.

Stateless por princípio

O serviço de domínio não guarda estado entre chamadas, porque representa uma capacidade do domínio,
e não uma coisa que tem identidade e ciclo de vida. Ele pode receber dependências, como um
verificador de limites ou uma tabela de tarifas, mas essas dependências são conceitos de domínio e
não placeholders de infraestrutura. O estado que ele manipula vive nas entidades e objetos de valor
que recebe e devolve. Essa natureza sem estado simplifica testes, permite instância única sem
sincronização e torna a chamada previsível: o mesmo conjunto de entradas produz o mesmo conjunto de
saídas. Quando um serviço começa a acumular campos de estado, como resultado parcial entre chamadas
ou cache mutável de decisão, ele está virando outra coisa, geralmente uma entidade mal descoberta
ou um serviço de aplicação inchado.

Serviço de domínio versus serviço de aplicação

A confusão mais comum do bloco é fundir serviço de domínio com serviço de aplicação, e a diferença
é de responsabilidade, não de nome. O serviço de aplicação orquestra o caso de uso: recebe a
requisição, carrega agregados pelos repositórios, abre transação, chama o domínio e publica eventos.
Ele não decide regra, e sua lógica é de coordenação. O serviço de domínio contém regra de negócio
que não coube em entidade ou objeto de valor, e não conhece transação, HTTP ou fila. Um teste rápido
para classificar uma classe: se ela pode ser explicada usando apenas termos do negócio, é serviço
de domínio; se a explicação precisa de termos como transação, fila, API e repositório, é serviço de
aplicação.

Critério Serviço de domínio Serviço de aplicação
Responsabilidade Regra de negócio sem dono natural Orquestração do caso de uso
Conhece Entidades, objetos de valor, outros serviços de domínio Casos de uso, repositórios, transações, eventos
Estado Stateless Stateless
Vocabulário Exclusivamente da linguagem ubíqua Vocabulário técnico e de fluxo
Teste típico Unitário, com objetos de domínio de mentira Integração ou unitário com mocks de borda
Exemplo CalculadoraDeLimiteDeCredito ProcessarTransferenciaController

Exemplo clássico: transferência entre contas

O exemplo em Java mostra a divisão de responsabilidades em uma transferência: a regra da operação
vive no serviço de domínio, as regras de cada conta vivem nas contas e a orquestração vive no
serviço de aplicação.

// Serviço de domínio: a regra da transferência não pertence a nenhuma das contas
public final class TransferenciaEntreContas {
    public ComprovanteDeTransferencia executar(Conta origem, Conta destino, Dinheiro valor) {
        if (origem.mesmoId(destino)) {
            throw new TransferenciaParaAMesmaConta(origem.id());
        }
        if (!valor.ePositivo()) {
            throw new ValorDeTransferenciaInvalido(valor);
        }
        // cada conta aplica suas próprias invariantes: limite, saldo e bloqueio
        origem.debitar(valor);
        destino.creditar(valor);
        return new ComprovanteDeTransferencia(origem.id(), destino.id(), valor);
    }
}

// Conta: regra individual, sem conhecer a outra ponta
public final class Conta {
    public void debitar(Dinheiro valor) {
        if (bloqueada) throw new ContaBloqueada(id);
        if (!saldo.cobre(valor)) throw new SaldoInsuficiente(id, saldo, valor);
        saldo = saldo.subtrair(valor);
    }
}

// Serviço de aplicação: orquestra, não decide regra
public final class ProcessarTransferencia {
    private final RepositorioDeContas contas;
    private final UnitOfWork transacao;

    public void processar(ContaId origemId, ContaId destinoId, Dinheiro valor) {
        try (var tx = transacao.abrir()) {                     // transação é detalhe de aplicação
            var origem = contas.buscarPorId(origemId).orElseThrow(ContaInexistente::new);
            var destino = contas.buscarPorId(destinoId).orElseThrow(ContaInexistente::new);
            var comprovante = new TransferenciaEntreContas().executar(origem, destino, valor);
            contas.atualizar(origem);
            contas.atualizar(destino);
            eventos.registrar(new TransferenciaRealizada(comprovante));
            tx.confirmar();
        }
    }
}

Se amanhã o negócio criar tarifa por transferência, a regra entra no serviço de domínio, e o
serviço de aplicação continua exatamente igual.

Políticas e especificações

Duas formas frequentes de serviço de domínio merecem destaque por aparecerem em quase todo sistema
maduro. A política encapsula uma decisão que o negócio muda com frequência, como política de
aprovação de crédito ou política de reembolso, e expõe um método único de decisão sobre objetos de
domínio, o que isola a mudança em um arquivo e facilita versionar regras por data. A especificação
encapsula um predicado reutilizável, como ClienteElegivelParaUpgrade, que serve tanto para filtrar
consultas quanto para validar em tempo de execução, evitando que o mesmo critério exista em SQL e
em código com interpretações divergentes. Ambas são stateless, testáveis isoladamente e nomeadas
pela linguagem ubíqua.

Erros comuns e boas práticas

  • Transformar o serviço de domínio em depósito de todas as regras. Serviço com dezenas de métodos
    e grandes blocos de lógica é modelo anêmico com outro nome, e as regras devem voltar às entidades.
  • Misturar orquestração com decisão. Se o método abre transação, chama repositório e também decide
    tarifa, ele é duas classes, e a tarifa está no lugar errado.
  • Nomear serviços com termos técnicos, como Processador ou Handler. O nome deve ser uma frase que o
    especialista do negócio reconheça, como ConciliadorDePagamentos.
  • Passar primitivos crus onde o domínio espera conceitos. O método que recebe três Strings e um
    BigDecimal perde a proteção dos objetos de valor que o sistema já construiu.
  • Deixar o serviço de domínio depender de infraestrutura direta, como cliente HTTP e driver de
    banco. Dependências externas entram por portas, e a implementação concreta fica na borda.
  • Criar serviço de domínio para lógica de um único agregado que cabe nele. A regra da lista de
    decisões é simples: entidade primeiro, objeto de valor depois, serviço por último.