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 { 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 Success(T value) => new(value); public static Result Failure(string errorMessage) => new(errorMessage); } 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 _users = new() { { 1, "Henrique" }, { 2, "Marta" } }; public Result GetUserById(int id) { if (!_users.TryGetValue(id, out var user)) { return Result.Failure("Usuário não encontrado."); } return Result.Success(user); } } 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 }); } } Exemplo de Requisição e Resposta Requisição: GET /api/users/1 Resposta (200 OK): { "user": "Henrique" } Caso não encontre: GET /api/users/99 Resposta (404 Not Found): { "error": "Usuário não encontrado." } 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; } } Agora alteramos Result para incluir um objeto Error: public class Result { 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 Success(T value) => new(value); public static Result Failure(string code, string message) => new(new Error(code, message)); } Agora podemos usar códigos de erro mais descritivos: public Result GetUserById(int id) { if (!_users.TryGetValue(id, out var user)) { return Result.Failure("USER_NOT_FOUND", "Usuário não encontrado."); } return Result.Success(user); } 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 }); } Resposta agora: { "errorCode": "USER_NOT_FOUND", "message": "Usuário não encontrado." } 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!

Feb 5, 2025 - 14:51
 0
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);
}

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);
    }
}

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 });
    }
}

Exemplo de Requisição e Resposta

Requisição:

GET /api/users/1

Resposta (200 OK):

{
    "user": "Henrique"
}

Caso não encontre:

GET /api/users/99

Resposta (404 Not Found):

{
    "error": "Usuário não encontrado."
}

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;
    }
}

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));
}

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);
}

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 });
}

Resposta agora:

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

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!