← Volver a Back.Domain

ServiceResult

Resultado genérico de servicios con manejo de errores

📊 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

📋 Propiedades de ServiceResult<T>

Data

T?
Datos del resultado (solo presente en caso de éxito)

IsSuccess

bool
Indica si la operación fue exitosa

Error

ServiceError?
Información del error (solo presente en caso de fallo)

🔧 Métodos de ServiceResult<T>

Success

public static ServiceResult<T> Success(T data)
Crea un resultado exitoso con los datos proporcionados

Failure

public static ServiceResult<T> Failure(ServiceError error)
Crea un resultado fallido con el error proporcionado

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

🔗 Integración con Otros Componentes

📚 Componentes Relacionados

  • Back.API: Controladores que utilizan ServiceResult
  • Back.Application: Servicios que devuelven ServiceResult
  • Middleware: Manejo global de errores
  • Validators: Validaciones que devuelven ServiceResult

🎯 Casos de Uso

  • Servicios de Negocio: Resultados de operaciones
  • Validaciones: Resultados de validación
  • APIs REST: Respuestas estandarizadas
  • Manejo de Errores: Gestión centralizada
  • Logging: Registro de errores
  • Testing: Pruebas de servicios

🔧 Consideraciones Técnicas

⚙️ Patrón Result

  • Inmutabilidad: Los objetos no se pueden modificar después de la creación
  • Tipado Seguro: Uso de genéricos para tipo de datos
  • Factory Methods: Métodos estáticos para crear instancias
  • Encapsulación: Propiedades de solo lectura

🔒 Beneficios

  • Consistencia: Formato uniforme en toda la aplicación
  • Mantenibilidad: Fácil de mantener y extender
  • Testabilidad: Fácil de probar
  • Legibilidad: Código más claro y expresivo