Application Services
Definição
Seção intitulada “Definição”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.
O que faz / o que não faz
Seção intitulada “O que faz / o que não faz”| 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) |
A forma canônica
Seção intitulada “A forma canônica”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;
@Servicepublic 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.
Diagrama
Seção intitulada “Diagrama”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
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Application service gordo: os
ifde negócio migram para ele e as entidades viram sacos de getters — o modelo anêmico de Entidades. - God service por recurso:
PedidoServicecom 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 depedido.cancelar(). - Dois agregados na mesma transação sem decisão consciente: ver a discussão em Agregados e em Exemplo Hexagonal com Spring.
Relações
Seção intitulada “Relações”- Contraste direto: Domain Services (regra sem dono vs. orquestração)
- Equivalentes arquiteturais: interactor de Use Cases na Clean; implementação da driving port em Ports
- Ferramentas que orquestra: Repositórios, Agregados, Domain Events
- Parte de Domain-Driven Design
- Voltar para Home
Perguntas de revisão
Seção intitulada “Perguntas de revisão”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.