Pular para o conteúdo
.NETPadrão de resultado e fluxo de resposta
Módulo 07Tópicos Avançados (Bônus)

Padrão de resultado e fluxo de resposta

avancado 40 min de leitura·Atualizado · .NET 9
Resumo

Exceções para controlar fluxo comum têm custo. O Result Pattern representa sucesso/falha como dados, evitando exceções em caminhos esperados e melhorando o desempenho sob carga.

1. Objetivos de Aprendizagem#

  • Entender o custo de usar exceções para fluxo esperado.
  • Implementar um tipo Result/ApiBaseResponse.
  • Refatorar serviço e controller para o fluxo baseado em resultado.

2. Pré-requisitos#

  • Criação de recursos e exceções de domínio (Módulos 06/09).

3. Conceito#

Lançar exceções é caro (captura de stack trace). Para fluxos esperados e frequentes (ex.: "não encontrado" num endpoint muito acessado), o Result Pattern retorna um objeto que descreve o desfecho, e o controller decide o status code. O porquê: menor overhead e fluxo explícito. Exceções permanecem para o que é realmente excepcional.

Fluxo baseado em resultado, sem exceções no caminho esperado:

Carregando diagrama…

4. Mão na Massa#

4.1. Setup#

Sem pacotes novos (implementação própria).

4.2. Implementação Passo a Passo#

  1. Tipos de resposta:
C#
public abstract class ApiBaseResponse
{
    public bool Success { get; }
    protected ApiBaseResponse(bool success) => Success = success;
}

public sealed class ApiOkResponse<T> : ApiBaseResponse
{
    public T Result { get; }
    public ApiOkResponse(T result) : base(true) => Result = result;
}

public sealed class ProdutoNotFoundResponse : ApiBaseResponse
{
    public Guid Id { get; }
    public ProdutoNotFoundResponse(Guid id) : base(false) => Id = id;
}
  1. Serviço retornando ApiBaseResponse:
C#
public async Task<ApiBaseResponse> GetAsync(Guid id, bool trackChanges)
{
    var produto = await _repo.Produto.GetByIdAsync(id, trackChanges);
    if (produto is null) return new ProdutoNotFoundResponse(id);
    return new ApiOkResponse<ProdutoDto>(_mapper.Map<ProdutoDto>(produto));
}
  1. Controller traduz o resultado em HTTP:
C#
[HttpGet("{id:guid}")]
public async Task<IActionResult> Get(Guid id)
{
    var response = await _service.Produto.GetAsync(id, false);
    if (!response.Success && response is ProdutoNotFoundResponse nf)
        return NotFound($"Produto {nf.Id} não encontrado.");

    return Ok(((ApiOkResponse<ProdutoDto>)response).Result);
}

4.3. Executando#

Sob carga, endpoints que antes lançavam exceções para "não encontrado" passam a responder sem o custo delas.

5. Exemplo Completo#

Um helper no controller pode centralizar a tradução de ApiBaseResponseIActionResult, evitando if/cast repetidos.

6. Boas Práticas e Armadilhas#

FaçaEvite
Result Pattern para fluxos esperados/frequentesExceções em caminhos comuns de alto tráfego
Reservar exceções para o realmente excepcionalTrocar tudo por Result sem necessidade
Medir antes e depois (benchmark)Otimização sem evidência
Atenção

não é "exceções são ruins". Para erros raros e de infraestrutura, exceções + handler global (Módulo 19) continuam sendo a melhor escolha. Result brilha em fluxos esperados de alto volume.

7. Segurança e Produção#

  • Otimize com base em métricas reais (profiling), não em suposições.
  • Mantenha o handler global para o que escapa do fluxo de resultado.

8. Exercícios#

  • Fácil: adicione um ApiBadRequestResponse.
  • Médio: crie um helper de tradução ProcessError no ControllerBase.
  • Desafio: faça um benchmark (BenchmarkDotNet) comparando exceção vs. Result no caminho "não encontrado".

9. Resumo#

O Result Pattern evita o custo de exceções em fluxos esperados de alto tráfego, retornando desfechos como dados que o controller traduz em status HTTP. Exceções permanecem para o excepcional.

10. Próximos Passos#

Módulo 44 (Bônus): CQRS e MediatR.

11. Referências#

44-01 — Padrão de resultado e fluxo de resposta | Curso ASP.NET Core