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!

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay