Pular para o conteúdo

Use Cases

Um interactor é a implementação concreta de um caso de uso. Ele contém as Application Business Rules: a sequência de passos necessária para cumprir um objetivo do usuário (ex.: criar pedido, transferir valor). Ele não sabe de HTTP, de SQL nem de JSON — só coordena Entities e portas.

Regra de ouro: um interactor por caso de uso mantém a OCP — adicionar um novo caso de uso é adicionar uma classe, não editar uma existente.

As fronteiras do caso de uso são interfaces definidas dentro do próprio anel de use cases, o que permite a inversão da A Regra da Dependência.

  • Input port (input boundary): a interface que o mundo externo chama para acionar o caso de uso. O controller depende dela; o interactor a implementa.
  • Output port (output boundary): a interface que o interactor chama para entregar o resultado. O interactor depende dela; o presenter a implementa.
graph LR
    C["Controller<br/>(Interface Adapter)"] -->|chama| IP["InputPort<br/>(interface, núcleo)"]
    IP -. implementado por .- I["Interactor<br/>(Use Case)"]
    I -->|chama| OP["OutputPort<br/>(interface, núcleo)"]
    OP -. implementado por .- P["Presenter<br/>(Interface Adapter)"]

Observe que as duas setas de dependência de código apontam para dentro (para as interfaces do núcleo), mesmo que o fluxo de controle atravesse controller → interactor → presenter.

Os dados que cruzam as boundaries são estruturas simples e desacopladas:

  • Request model: DTO de entrada montado pelo controller e recebido pelo input port. Não é o @RequestBody; não é a Entity.
  • Response model: DTO de saída produzido pelo interactor e entregue ao output port. Não é a Entity; não é ResponseEntity.

Isso mantém a Entity protegida e evita que a forma de UI ou de persistência contamine o núcleo (ver DTOs cruzando fronteiras em A Regra da Dependência).

O presenter vive no anel de Interface Adapters e implementa o output port. Ele recebe o response model e o transforma no view model — a forma exata que a tela ou a resposta HTTP precisa (strings formatadas, datas locais, flags de exibição). A vantagem do presenter sobre um simples return é permitir que o interactor não dependa da forma de apresentação; a lógica de formatação fica testável e fora do núcleo.

Em muitos projetos Spring pragmáticos, o output port é simplificado para um valor de retorno do interactor e um mapper no controller. A forma canônica com presenter separado é preferível quando a apresentação tem lógica própria ou múltiplas saídas (web, CLI, relatório).

Os casos de uso da Clean são exatamente os casos de uso expostos pelas driving Ports da Arquitetura Hexagonal:

  • Input port da Clean = driving port (porta de entrada) hexagonal.
  • Output port / gateway da Clean = driven port (porta de saída) hexagonal.
  • O interactor é o núcleo aplicacional que a Hexagonal esconde atrás das portas.

Ver Driving e Driven para a simetria e Comparação - Hexagonal vs Clean vs Camadas para o mapeamento completo.

public interface CriarPedidoInputPort {
void executar(CriarPedidoRequest request);
}
public interface CriarPedidoOutputPort {
void apresentar(PedidoResponse response);
}
public record CriarPedidoRequest(String clienteId, List<ItemRequest> itens) {}
public record ItemRequest(String produtoId, int quantidade, BigDecimal precoUnitario) {}
public record PedidoResponse(String pedidoId, String status, BigDecimal total) {}

O interactor completo, com gateway e Entity, aparece em Exemplo Clean com Spring.

  • Interactor que retorna ResponseEntity ou fala JSON: o caso de uso sabendo de web.
  • God interactor: uma classe orquestrando vários casos de uso, ferindo SRP e OCP.
  • Request model = Entity: reaproveitar a Entity como DTO de entrada, acoplando a API ao domínio.
  • Regra de negócio no interactor que deveria estar na Entity: o interactor orquestra; a regra corporativa é da Entity.
  • Output port ignorado: interactor com dependência direta do presenter concreto em vez da interface, quebrando a inversão.

1. Quem implementa o input port e quem depende dele?

Resposta

O interactor implementa o input port; o controller depende dele. Como a interface está no núcleo, a dependência do controller aponta para dentro.

2. Qual a diferença entre request model e a Entity?

Resposta

O request model é um DTO simples de transporte, na forma conveniente ao caso de uso, sem comportamento de negócio. A Entity carrega regras de negócio corporativas. Nunca se usa a Entity como DTO de fronteira.

3. Para que serve o presenter em vez de um simples return?

Resposta

Para desacoplar a formatação de apresentação do interactor. O presenter (anel de adapters) implementa o output port e produz o view model, mantendo o núcleo independente da forma de exibição e permitindo múltiplas saídas.

4. Como um caso de uso da Clean se mapeia na Hexagonal?

Resposta

O input port equivale a uma driving port (entrada) e o output port/gateway a uma driven port (saída). O interactor é o serviço de aplicação escondido atrás dessas portas.