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!
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!