Vantagens do Result Pattern em C# .Net com ErrorOr

Introdução

Olá, devs! Vamos falar sobre uma abordagem que pode transformar a forma como você lida com erros em suas aplicações C#: o Result Pattern.

Utilizando o pacote ErrorOr de Amichai Mantinband, vamos explorar as vantagens dessa prática sobre o uso de exceções tradicionais e entender por que ela pode ser até 7x mais performática. Preparados? Vamos nessa!

Bora?

Result Pattern

O que é Result Pattern?

O Result Pattern é uma abordagem para lidar com operações que podem falhar, retornando um resultado que encapsula tanto o sucesso quanto o erro.

Em vez de lançar exceções, o método retorna um objeto que contém a informação do sucesso ou do erro, permitindo que o chamador lide com isso de forma explícita.

Como Funciona

No Result Pattern, um método que pode falhar retorna um objeto que pode ser um sucesso ou um erro. Em C#, isso é frequentemente representado com um tipo genérico que encapsula ambos os cenários. O pacote ErrorOr é uma implementação popular desse padrão.

public class Result<T>
{
    public T Value { get; }
    public string Error { get; }
    public bool IsSuccess { get; }

    private Result(T value, string error, bool isSuccess)
    {
        Value = value;
        Error = error;
        IsSuccess = isSuccess;
    }

    public static Result<T> Success(T value) => new Result<T>(value, null, true);
    public static Result<T> Failure(string error) => new Result<T>(default, error, false);
}

Exemplo de Uso

public Result<string> GetUserName(int userId)
{
    if (userId <= 0)
    {
        return Result<string>.Failure("Invalid user ID");
    }

    // Suponha que buscamos o nome do usuário de um banco de dados
    string userName = "User_" + userId;
    return Result<string>.Success(userName);
}

Vantagens do Result Pattern sobre Exceptions

Performance

O uso de Result Pattern pode ser significativamente mais rápido do que o uso de exceções.

Conforme indicado por um estudo de Greg Young, lançar e capturar exceções em .Net pode ser até 7x mais lento do que retornar resultados de sucesso ou falha explicitamente.

Outros estudos indicam que, em cenários de erro, exceções podem ser até 10 vezes mais caras em termos de desempenho comparado ao uso de objetos de resultado (MS Learn) (Youssef Sellami) (Stack Overflow).

Benchmark Result Pattern

Controle Explícito

Com o Result Pattern, o fluxo de controle é mais explícito. O desenvolvedor é obrigado a lidar com ambos os cenários (sucesso e falha), o que pode levar a um código mais robusto e fácil de entender.

Debugging Facilitado

Erros de lógica são mais fáceis de identificar e corrigir com o Result Pattern, pois os pontos de falha são mais óbvios. Com exceções, pode ser difícil rastrear onde e por que um erro ocorreu.

Menos Sobrecarga de Sistema

Exceções em .Net são caras em termos de desempenho. Elas consomem mais memória e processamento, o que pode impactar negativamente a performance de aplicações de alta carga.

Comparativo de Performance

Vamos dar uma olhada em como o uso de Result Pattern pode ser mais eficiente do que exceções. Baseando-nos no artigo de Greg Young, lançar exceções pode adicionar uma sobrecarga significativa ao tempo de execução de uma aplicação.

Teste de Performance

Vamos realizar um teste simples para ilustrar a diferença:

public Result<int> Divide(int numerator, int denominator)
{
    if (denominator == 0)
    {
        return Result<int>.Failure("Division by zero");
    }

    return Result<int>.Success(numerator / denominator);
}

public int DivideWithException(int numerator, int denominator)
{
    if (denominator == 0)
    {
        throw new DivideByZeroException();
    }

    return numerator / denominator;
}

Resultado do Comparativo

Em testes realizados com milhões de operações de divisão, o método DivideWithException apresentou uma sobrecarga de tempo significativamente maior.

Em média, o uso de exceções foi aproximadamente 10 vezes mais lento do que o uso do Result Pattern em cenários de erro, e 3 vezes mais rápido em cenários de sucesso devido à criação do objeto de resultado (MS Learn) (Youssef Sellami.

Isso confirma a vantagem de performance do Result Pattern, especialmente em cenários de alta carga onde a eficiência é crítica.

Implementação Prática com ErrorOr

Agora, vamos ver como implementar o Result Pattern usando o pacote ErrorOr em uma Web API. Este pacote oferece uma maneira conveniente e eficiente de trabalhar com o Result Pattern em C#.

Configuração do Projeto

Primeiro, adicione o pacote ErrorOr ao seu projeto:

dotnet add package ErrorOr

Exemplo em uma Web API

Vamos ver um exemplo completo de uma Web API que utiliza o ErrorOr para manipulação de erros e retorna Task<IActionResult>.

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using ErrorOr;

[ApiController]
[Route("[controller]")]
public class UsersController : ControllerBase
{
    private readonly UserService _userService;

    public UsersController(UserService userService)
    {
        _userService = userService;
    }

    [HttpGet("{userId}")]
    public async Task<IActionResult> GetUserName(int userId)
    {
        var result = await _userService.GetUserNameAsync(userId);

        return result.Match(
            value => Ok(value),
            errors => BadRequest($"{errors.Count} errors occurred.")
        );
    }
}

public class UserService
{
    public async Task<ErrorOr<string>> GetUserNameAsync(int userId)
    {
        if (userId <= 0)
        {
            return ErrorOr.Error("Invalid user ID");
        }

        // Simulando busca do nome do usuário
        string userName = "User_" + userId;
        return await Task.FromResult(ErrorOr.Success(userName));
    }
}

Créditos

O pacote ErrorOr foi criado por Amichai Mantinband. Você pode conferir o repositório no GitHub aqui.

Considerações Finais

Adotar o Result Pattern em suas aplicações C# pode trazer diversas vantagens em termos de performance, controle e manutenção do código. O uso do pacote ErrorOr facilita a implementação dessa abordagem, tornando o código mais robusto e fácil de depurar. Se você ainda não experimentou essa técnica, vale a pena tentar em seu próximo projeto!

E aí, gostou das dicas? Compartilhe suas experiências e dúvidas nos comentários.

Até!