Pular para o conteúdo

Application Services

O Application Service vive na camada de aplicação, entre a borda e o domínio. Ele representa um caso de uso completo do ponto de vista do sistema (“confirmar pedido”, “transferir valor”) e é o lugar natural da fronteira transacional: tudo que o caso de uso faz acontece ou desfaz junto.

Sua relação com os outros dois “services” está na tabela de Domain Services: o Domain Service carrega regra sem dono natural; o Application Service carrega coordenação; o Infrastructure Service carrega tecnologia.

Faz Não faz
Define a fronteira transacional (@Transactional) Conter if de regra de negócio
Carrega e salva agregados via Repositórios Conhecer HTTP, JSON, SQL
Delega decisões a Entidades, Value Objects e Domain Services Manipular estado interno de agregado por setters
Publica Domain Events após persistir Chamar outro application service
Traduz domínio → DTO de resposta Validar formato de payload (isso é da borda — Adapters)

Buscar → delegar → salvar → publicar. O corpo se lê como uma frase:

package com.loja.pedido.application;
import com.loja.pedido.domain.Pedido;
import com.loja.pedido.domain.PedidoId;
import com.loja.pedido.domain.PedidoRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CancelarPedidoService {
private final PedidoRepository pedidoRepository;
private final ApplicationEventPublisher publisher;
public CancelarPedidoService(PedidoRepository pedidoRepository, ApplicationEventPublisher publisher) {
this.pedidoRepository = pedidoRepository;
this.publisher = publisher;
}
@Transactional
public void executar(PedidoId pedidoId) {
Pedido pedido = pedidoRepository.porId(pedidoId)
.orElseThrow(() -> new IllegalArgumentException("Pedido inexistente"));
pedido.cancelar();
pedidoRepository.salvar(pedido);
pedido.eventosNaoPublicados().forEach(publisher::publishEvent);
pedido.limparEventos();
}
}

Busque o pedido, cancele, salve, publique o que aconteceu. Se explicar o método exigir enunciar uma regra (“cancela se ainda não foi enviado…”), a regra está no lugar errado — ela pertence a pedido.cancelar().

Um service por caso de uso (CancelarPedidoService, ConfirmarPedidoService) mantém SRP e OCP: caso de uso novo é classe nova, não mais um método num PedidoService que cresce para sempre.

sequenceDiagram
    participant Borda as Adapter primario
    participant AS as Application Service
    participant Repo as Repositorio (port)
    participant Agg as Agregado
    participant Bus as EventPublisher

    Borda->>AS: executar(comando)
    AS->>Repo: porId(id)
    Repo-->>AS: agregado
    AS->>Agg: decidir (metodo de negocio)
    AS->>Repo: salvar(agregado)
    AS->>Bus: publicar eventos registrados
    AS-->>Borda: DTO de resposta
  • Application service gordo: os if de negócio migram para ele e as entidades viram sacos de getters — o modelo anêmico de Entidades.
  • God service por recurso: PedidoService com 30 métodos misturando todos os casos de uso do agregado.
  • Service chamando service: sinal de regra sem dono ou de fluxo que deveria ser reação a evento; extraia um Domain Service ou reaja via Domain Events.
  • Pular o agregado: pedido.setStatus(CANCELADO) no service em vez de pedido.cancelar().
  • Dois agregados na mesma transação sem decisão consciente: ver a discussão em Agregados e em Exemplo Hexagonal com Spring.

1. Qual é a forma canônica de um Application Service?

Resposta

Buscar o agregado via Repositórios → delegar a decisão ao domínio → salvar → publicar Domain Events → responder com DTO. Tudo dentro de uma fronteira transacional, sem regra de negócio própria.

2. Como perceber que regra de negócio vazou para o Application Service?

Resposta

Quando descrever o método exige enunciar uma regra (“só se”, “a menos que”) ou quando há if sobre estado do agregado. A decisão pertence a Entidades, Value Objects ou Domain Services; o service só coordena.

3. Por que um service por caso de uso em vez de um PedidoService geral?

Resposta

SRP e OCP: cada caso de uso muda por motivos próprios. Classe nova por caso de uso evita o god service e mantém o sistema aberto a extensão sem edição do existente.

4. O que fazer quando um Application Service “precisa” chamar outro?

Resposta

Repensar o desenho: ou existe um Domain Service escondido (regra que cruza agregados), ou o segundo passo é reação a um fato consumado — caso para Domain Events e consistência eventual.