DEV Community

Cover image for Aplicando o padrão Result em APIs com o .NET 9.0
Henrique Mauri
Henrique Mauri

Posted on

Aplicando o padrão Result em APIs com o .NET 9.0

O uso do padrão Result em APIs melhora a manutenção do código e padroniza o retorno de operações. Com o .NET 9.0, podemos estruturar nossas APIs para lidar com sucesso, erros de negócio e falhas inesperadas de forma elegante.

O Que é o Padrão Result?

O padrão Result encapsula o resultado de uma operação, permitindo que seja retornado um objeto contendo:

Sucesso com um valor

Erro de negócio (exemplo: dados inválidos)

Falha inesperada (exemplo: exceções)

Isso evita o uso excessivo de exceções e melhora a previsibilidade do código.

Implementando o Padrão Result

Vamos começar definindo uma classe genérica Result:

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

    private Result(T value)
    {
        IsSuccess = true;
        Value = value;
    }

    private Result(string errorMessage)
    {
        IsSuccess = false;
        ErrorMessage = errorMessage;
    }

    public static Result<T> Success(T value) => new(value);
    public static Result<T> Failure(string errorMessage) => new(errorMessage);
}
Enter fullscreen mode Exit fullscreen mode

Agora podemos usar essa estrutura nos serviços da nossa API.

Aplicando o Padrão em um Service

Crie um serviço que usa Result:

public class UserService
{
    private readonly Dictionary<int, string> _users = new()
    {
        { 1, "Henrique" },
        { 2, "Marta" }
    };

    public Result<string> GetUserById(int id)
    {
        if (!_users.TryGetValue(id, out var user))
        {
            return Result<string>.Failure("Usuário não encontrado.");
        }
        return Result<string>.Success(user);
    }
}
Enter fullscreen mode Exit fullscreen mode

Integrando o Padrão Result em um Controller

Agora usamos esse serviço em um **controller **no ASP.NET Core 9.0:

[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
    private readonly UserService _userService = new();

    [HttpGet("{id}")]
    public IActionResult GetUser(int id)
    {
        var result = _userService.GetUserById(id);

        if (!result.IsSuccess)
        {
            return NotFound(new { error = result.ErrorMessage });
        }

        return Ok(new { user = result.Value });
    }
}
Enter fullscreen mode Exit fullscreen mode

Exemplo de Requisição e Resposta

Requisição:

GET /api/users/1

Resposta (200 OK):

{
    "user": "Henrique"
}
Enter fullscreen mode Exit fullscreen mode

Caso não encontre:

GET /api/users/99

Resposta (404 Not Found):

{
    "error": "Usuário não encontrado."
}
Enter fullscreen mode Exit fullscreen mode

Melhorando com Tipos de Erro

Podemos adicionar uma classe de erros mais detalhada:

public class Error
{
    public string Code { get; }
    public string Message { get; }

    public Error(string code, string message)
    {
        Code = code;
        Message = message;
    }
}
Enter fullscreen mode Exit fullscreen mode

Agora alteramos Result para incluir um objeto Error:

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

    private Result(T value)
    {
        IsSuccess = true;
        Value = value;
    }

    private Result(Error error)
    {
        IsSuccess = false;
        Error = error;
    }

    public static Result<T> Success(T value) => new(value);
    public static Result<T> Failure(string code, string message) => new(new Error(code, message));
}
Enter fullscreen mode Exit fullscreen mode

Agora podemos usar códigos de erro mais descritivos:

public Result<string> GetUserById(int id)
{
    if (!_users.TryGetValue(id, out var user))
    {
        return Result<string>.Failure("USER_NOT_FOUND", "Usuário não encontrado.");
    }
    return Result<string>.Success(user);
}
Enter fullscreen mode Exit fullscreen mode

E o controller agora responde de forma mais estruturada:

[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
    var result = _userService.GetUserById(id);

    if (!result.IsSuccess)
    {
        return NotFound(new { errorCode = result.Error!.Code, message = result.Error.Message });
    }

    return Ok(new { user = result.Value });
}
Enter fullscreen mode Exit fullscreen mode

Resposta agora:

{
    "errorCode": "USER_NOT_FOUND",
    "message": "Usuário não encontrado."
}
Enter fullscreen mode Exit fullscreen mode

Conclusão

O uso do padrão Result no .NET 9.0 melhora a previsibilidade do código e evita o uso excessivo de exceções. Ele permite um tratamento estruturado de sucesso e falhas, deixando o código mais limpo e manutenção mais simples.

Se você já usa esse padrão ou tem dúvidas, deixe um comentário!

Top comments (0)