No post anterior, compartilhei um exemplo de como containers podem nos ajudar a deixar o código mais claro sobre os resultados de um método.
public interface IEmployeeRepository
{
Option<Employee> GeyById(string id);
}
class EmployeeRepository : IEmployeeRepository
{
public Option<Employee> GeyById(string id)
=>; new DbContext().Find(id);
}
E o que temos sobre Exceptions?! O método GetById irá lançar uma exception se alguma coisa der errado (se não for possível conectar com o banco, por exemplo), mas não há nada sobre isso na assinatura do método.
Either
Uma abordagem superior seria:
public interface IEmployeeRepository { Either<Exception, Option<Employee>> GeyById(string id); } class EmployeeRepository : IEmployeeRepository { public Either<Exception, Option<Employee>> GeyById(string id) { try { return new DbContext().Find(id); } catch (Exception e) { return e; } } }
O tipo Either é apenas uma struct comum com conversões implícitas e algumas operações funcionais. Ele permite diferentes tipos de retorno. Neste exemplo, estamos usando o container para tornar explícito que o método pode resultar tanto uma instância de Employee quanto uma exceção.
Try
Eu amo o Either, mas ele não é bom o suficiente! Eu tenho trabalhando bastante para tornar o meu código ainda mais claro.
public interface IEmployeeRepository
{
Try<Exception, Option<Employee>> GeyById(string id);
}
class EmployeeRepository : IEmployeeRepository
{
public Try<Exception, Option<Employee>> GeyById(string id)
=> Try.Run(return new DbContext().Find(id));
}
O tipo Try, assim como Either, é uma struct que provê conversões implícitas e algumas operações muito básicas. Ela permite que diferentes tipos de retorno para falha e para sucesso.
A coisa mais interessante sobre esta abordagem é que os programadores que consomem EmployeeRepository são agora forçados a tratar exceções.
public IActionResult Get(string id) => _repository.GetById(id).Match<IActionResult>(
failure: _ => DatabaseError(),
success: e => e .Match<IActionResult>(
some: employee => Ok(employee),
none: () => NotFound()
));
Implementações de Either e Try estão disponíveis no meu github.