📊 Descripción General
La clase ServiceResult<T> es un patrón de diseño que proporciona una forma estandarizada de manejar los resultados de los servicios en el sistema RestMaster. Esta clase encapsula tanto los datos de éxito como los errores, permitiendo un manejo consistente de respuestas en toda la aplicación.
🎯 Propósito de la Clase
- Resultados Estandarizados: Formato consistente para respuestas
- Manejo de Errores: Gestión centralizada de errores
- Tipado Seguro: Resultados tipados genéricamente
- Patrón Result: Implementación del patrón Result
- Interoperabilidad: Compatible con APIs REST
📋 Clase Relacionada: ServiceError
🎯 Propósito de ServiceError
La clase ServiceError encapsula la información de errores que pueden ocurrir en los servicios, proporcionando un formato estandarizado para el manejo de errores.
Message
string
Mensaje descriptivo del error
Code
string
Código único del tipo de error
Details
object?
Detalles adicionales del error (opcional)
NotFound
public static ServiceError NotFound(string message, object? details = null)
Crea un error de recurso no encontrado
ValidationError
public static ServiceError ValidationError(string message, object? details = null)
Crea un error de validación
BusinessRule
public static ServiceError BusinessRule(string message, object? details = null)
Crea un error de regla de negocio
💡 Ejemplo de Uso
// Ejemplo de servicio que devuelve ServiceResult
public class ArticuloService
{
public async Task<ServiceResult<Articulo>> GetArticuloByIdAsync(int id)
{
try
{
var articulo = await _repository.GetByIdAsync(id);
if (articulo == null)
{
return ServiceResult<Articulo>.Failure(
ServiceError.NotFound($"Artículo con ID {id} no encontrado")
);
}
return ServiceResult<Articulo>.Success(articulo);
}
catch (Exception ex)
{
return ServiceResult<Articulo>.Failure(
ServiceError.BusinessRule("Error al obtener el artículo", ex.Message)
);
}
}
public async Task<ServiceResult<Articulo>> CreateArticuloAsync(Articulo articulo)
{
try
{
// Validaciones
if (string.IsNullOrEmpty(articulo.Nombre))
{
return ServiceResult<Articulo>.Failure(
ServiceError.ValidationError("El nombre del artículo es requerido")
);
}
if (articulo.Precio <= 0)
{
return ServiceResult<Articulo>.Failure(
ServiceError.ValidationError("El precio debe ser mayor a 0")
);
}
var createdArticulo = await _repository.CreateAsync(articulo);
return ServiceResult<Articulo>.Success(createdArticulo);
}
catch (Exception ex)
{
return ServiceResult<Articulo>.Failure(
ServiceError.BusinessRule("Error al crear el artículo", ex.Message)
);
}
}
}
// Uso en el controlador
[HttpGet("{id}")]
public async Task<IActionResult> GetArticulo(int id)
{
var result = await _articuloService.GetArticuloByIdAsync(id);
if (result.IsSuccess)
{
return Ok(result.Data);
}
return result.Error?.Code switch
{
"NotFound" => NotFound(result.Error.Message),
"ValidationError" => BadRequest(result.Error.Message),
"BusinessRule" => StatusCode(500, result.Error.Message),
_ => StatusCode(500, "Error interno del servidor")
};
}
// Ejemplo con múltiples resultados
public async Task<ServiceResult<List<Articulo>>> GetArticulosByFamiliaAsync(string familia)
{
try
{
var articulos = await _repository.GetByFamiliaAsync(familia);
if (!articulos.Any())
{
return ServiceResult<List<Articulo>>.Failure(
ServiceError.NotFound($"No se encontraron artículos para la familia {familia}")
);
}
return ServiceResult<List<Articulo>>.Success(articulos);
}
catch (Exception ex)
{
return ServiceResult<List<Articulo>>.Failure(
ServiceError.BusinessRule("Error al obtener artículos", ex.Message)
);
}
}
// Manejo de errores con detalles
public ServiceResult<Articulo> ValidateArticulo(Articulo articulo)
{
var errors = new List<string>();
if (string.IsNullOrEmpty(articulo.Nombre))
errors.Add("Nombre es requerido");
if (articulo.Precio <= 0)
errors.Add("Precio debe ser mayor a 0");
if (errors.Any())
{
return ServiceResult<Articulo>.Failure(
ServiceError.ValidationError("Errores de validación", errors)
);
}
return ServiceResult<Articulo>.Success(articulo);
}