.NET - O padrão ServiceResult


    Hoje veremos o padrão ServiceResult, o que é e para que serve.

O padrão Service Result é uma abordagem popular no design de software, especialmente em cenários onde você deseja retornar o resultado de uma operação e quaisquer possíveis erros ou exceções sem lançar exceções diretamente. Este padrão é particularmente útil em arquiteturas em camadas, como na construção de APIs ou serviços.

Este padrão fornece uma maneira padronizada para encapsular o resultado de uma chamada de serviço sendo útil para serviços que podem retornar valores, erros ou ambos.

O padrão ServiceResult é composto por duas classes:

Vejamos como implementar este padrão usando o seguinte código :

public struct ServiceResult<T>
{
 
public T? Data { get; }
 
public Exception? Error { get; }
 
public bool IsSuccess => Error == null;

  public ServiceResult(T data)
  {
     Data = data;
     Error =
null;
  }

   public ServiceResult(Exception error)
   {
      Data =
default;
      Error = error;
   }

    // operador Implicito para os dados
   
public static implicit operator ServiceResult<T>(T data)
                           =>
new ServiceResult<T>(data);

    // operador implicito para exceção
   
public static implicit operator ServiceResult<T>(Exception ex)
                            =>
new ServiceResult<T>(ex);
}

Neste código temos:

 -  Os dados contêm o resultado da operação.
 -  O erro contém qualquer exceção que possa ter ocorrido.
 -  IsSuccess é uma propriedade que indica se a operação foi bem-sucedida (ou seja, sem exceções).


Ao usar o operador implícito, tornamos o código UserService mais limpo e intuitivo. Em vez de criar explicitamente um novo ServiceResult<User> com dados ou uma exceção, simplesmente retornamos os dados ou a exceção diretamente, e a conversão implícita cuida do resto.

Nota: Embora o operador implícito possa tornar o código mais limpo, é essencial usá-lo criteriosamente. Usá-lo excessivamente ou de maneiras não intuitivas pode tornar o código mais difícil de entender para outros desenvolvedores. Sempre busque clareza e legibilidade.

Exemplo:

public class UserService
{
    public ServiceResult<User> GetUserById(int userId)
    {
        try
        {
            // Logica para retornar o usuário do banco de dados
            User user = UserRepository.GetUserById(userId);
            if (user != null)
            {
                // Retorna o user como resultado
                return ServiceResult<User>.Success(user);
            }
            else
            {
                // retorna o erro notfound como resultado
                return ServiceResult<User>.Error("User not found");
            }
        }
        catch (Exception ex)
        {
            // retorna a exceção como resultado
            return ServiceResult<User>.Exception(ex);
        }
    }
} 

Neste exemplo, a classe UserService possui um método GetUserById que recupera um usuário do banco de dados com base no userId fornecido. O método retorna um objeto ServiceResult<User>.

A classe ServiceResult<T> é uma classe genérica que encapsula o resultado da operação. Possui métodos de fábrica estáticos Success, Error e Exception para criar instâncias de ServiceResult<T> com resultados diferentes.

Ao usar o padrão ServiceResult, o chamador do método GetUserById pode manipular facilmente os diferentes resultados da operação. Por exemplo:

UserService userService = new UserService();
ServiceResult<User> result = userService.GetUserById(123);
if (result.Success)
{
    User user = result.Data;
    // Processa o objeto user
}
else if (result.Error)
{
    string errorMessage = result.ErrorMessage;
    // Trata o erro
}
else if (result.Exception)
{
    Exception exception = result.Exception;
    // Trata a exceção
}

Ao encapsular o resultado e possíveis erros ou exceções de maneira padronizada, o padrão ServiceResult melhora a legibilidade e a manutenção do código, facilitando o tratamento de diferentes resultados e erros em métodos de serviço.

Por que usar o padrão de resultado de serviço ?

Este padrão fornece uma maneira robusta e clara de lidar com os resultados da operação, facilitando a escrita e a manutenção do código pelos desenvolvedores e a compreensão dos resultados de suas chamadas pelos consumidores.

Pode ser usado para melhorar a legibilidade e manutenibilidade do código, facilitando a compreensão do resultado de uma chamada de serviço. Ele também pode ajudar a evitar erros de programação, garantindo que os erros sejam tratados adequadamente.

Essa abordagem pode ser particularmente benéfica em cenários como a construção de APIs, onde você deseja fornecer feedback claro ao cliente sobre o resultado de sua solicitação, sejam dados ou um erro detalhado.

E estamos conversados...

"Porque Deus enviou o seu Filho ao mundo, não para que condenasse o mundo, mas para que o mundo fosse salvo por ele."
João 3:17

Referências:


José Carlos Macoratti