Domain Services
Definição
Seção intitulada “Definição”Para Eric Evans, quando uma operação de domínio significativa não se encaixa como responsabilidade de uma entidade ou VO — tipicamente porque coordena vários agregados ou depende de conceitos que não têm um “dono” claro — ela vira um Domain Service. É uma operação, não uma coisa: seu nome costuma ser um verbo do domínio.
Vaughn Vernon alerta que Domain Services devem ser a exceção, não a regra. Antes de criar um, pergunte se o comportamento não caberia numa entidade ou VO. O abuso de services é a principal causa do modelo anêmico.
Três tipos de “service” (não confundir)
Seção intitulada “Três tipos de “service” (não confundir)”| Tipo | Camada | Responsabilidade |
|---|---|---|
| Domain Service | Domínio | Regra de negócio sem lar em entidade/VO; stateless |
| Application Service | Aplicação | Orquestra caso de uso, transação, coordena repositórios |
| Infrastructure Service | Infraestrutura | Detalhes técnicos: e-mail, fila, gateway |
O problema resolvido
Seção intitulada “O problema resolvido”Dá um lar a lógicas como: transferência entre duas Contas (envolve dois agregados), cálculo de rota, política de precificação que consulta várias fontes. Colocá-las em uma das entidades distorceria a responsabilidade; espalhá-las na camada de aplicação as tiraria do domínio.
Regras de design
Seção intitulada “Regras de design”- Stateless. Não guarda estado entre chamadas.
- Expresso na linguagem ubíqua.
ServicoDeTransferencia, nãoTransferHelper. - Opera sobre objetos de domínio, recebendo/retornando entidades e VOs.
- Sem I/O direto. Persistência e chamadas externas ficam na camada de aplicação/adapters; o Domain Service contém a regra, não a orquestração.
Trade-off / quando NÃO usar
Seção intitulada “Trade-off / quando NÃO usar”Se a regra pode viver numa entidade ou VO, prefira colocá-la lá. Domain Service é o último recurso quando o comportamento realmente não tem dono. Usá-lo por preguiça leva ao anêmico.
Exemplo em Java + Spring
Seção intitulada “Exemplo em Java + Spring”Transferência entre duas contas: comportamento que envolve dois agregados, logo não pertence a nenhum deles isoladamente. A regra está no Domain Service; a orquestração/transação fica no Application Service.
package com.banco.conta.domain;
import com.loja.shared.domain.Dinheiro;
public class ServicoDeTransferencia {
public void transferir(Conta origem, Conta destino, Dinheiro valor) { if (origem.id().equals(destino.id())) { throw new IllegalArgumentException("Origem e destino iguais"); } origem.debitar(valor); destino.creditar(valor); }}package com.banco.conta.domain;
import com.loja.shared.domain.Dinheiro;
public class Conta {
private final ContaId id; private Dinheiro saldo;
public Conta(ContaId id, Dinheiro saldoInicial) { this.id = id; this.saldo = saldoInicial; }
public void debitar(Dinheiro valor) { if (valor.maiorQue(saldo)) { throw new IllegalStateException("Saldo insuficiente"); } this.saldo = saldo.subtrair(valor); }
public void creditar(Dinheiro valor) { this.saldo = saldo.somar(valor); }
public ContaId id() { return id; }
public Dinheiro saldo() { return saldo; }}package com.banco.conta.application;
import com.banco.conta.domain.Conta;import com.banco.conta.domain.ContaId;import com.banco.conta.domain.ContaRepository;import com.banco.conta.domain.ServicoDeTransferencia;import com.loja.shared.domain.Dinheiro;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;
@Servicepublic class TransferenciaApplicationService {
private final ContaRepository contaRepository; private final ServicoDeTransferencia servicoDeTransferencia;
public TransferenciaApplicationService(ContaRepository contaRepository, ServicoDeTransferencia servicoDeTransferencia) { this.contaRepository = contaRepository; this.servicoDeTransferencia = servicoDeTransferencia; }
@Transactional public void executar(ContaId origemId, ContaId destinoId, Dinheiro valor) { Conta origem = contaRepository.porId(origemId) .orElseThrow(() -> new IllegalArgumentException("Conta origem inexistente")); Conta destino = contaRepository.porId(destinoId) .orElseThrow(() -> new IllegalArgumentException("Conta destino inexistente"));
servicoDeTransferencia.transferir(origem, destino, valor);
contaRepository.salvar(origem); contaRepository.salvar(destino); }}sequenceDiagram
participant AS as TransferenciaApplicationService
participant Repo as ContaRepository
participant DS as ServicoDeTransferencia
participant O as Conta origem
participant D as Conta destino
AS->>Repo: porId(origem/destino)
Repo-->>AS: Contas
AS->>DS: transferir(origem, destino, valor)
DS->>O: debitar(valor)
DS->>D: creditar(valor)
AS->>Repo: salvar(origem/destino)
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Modelo anêmico: mover toda a lógica para services e deixar entidades como sacos de dados.
- Confundir camadas: colocar transação/orquestração no Domain Service, ou regra de negócio no Application Service.
- Domain Service com estado: guardar dados entre chamadas.
- I/O no Domain Service: chamar banco/HTTP direto, acoplando o domínio à infraestrutura.
Relações
Seção intitulada “Relações”- Coordena Entidades e Agregados
- Distinto do Application Service, que usa Repositórios
- Nomeado pela Linguagem Ubíqua
- Bloco de Design Estratégico e Tático e parte de Domain-Driven Design
- Voltar para Home
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. Quando usar um Domain Service em vez de um método na entidade?
Resposta
Quando o comportamento não tem dono natural — tipicamente quando coordena vários agregados (ex.: transferência entre contas). Se cabe numa entidade ou VO, prefira colocar lá.
2. Qual a diferença entre Domain Service e Application Service?
Resposta
O Domain Service contém regra de negócio e é stateless, no domínio. O Application Service orquestra o caso de uso: transação, carga via Repositórios e coordenação, na camada de aplicação.
3. Por que Domain Services devem ser a exceção?
Resposta
Porque o abuso esvazia as entidades e leva ao modelo anêmico. A recomendação (Vernon) é preferir sempre comportamento nas entidades/VOs e recorrer ao service só quando não há dono.
4. Um Domain Service pode acessar o banco diretamente?
Resposta
Não. Ele contém a regra pura; I/O e persistência ficam na camada de aplicação e nos Adapters, preservando a pureza do domínio.