12/12/23
5 min de leitura
A importância de exceções personalizadas no gerenciamento de erros
Maurício FedattoQuando tive meu primeiro contato com programação orientada a objetos, em 2005, as exceções do Java já me saltaram aos olhos. Afinal, era uma forma de interromper um fluxo que sabemos que não tem como ser finalizado. No entanto, ao longo da minha carreira eu notei pouquíssimo o uso de exceções personalizadas. Pelo contrário, me deparei com inúmeras variáveis de tipos primitivos sendo informadas por referência para serem usados como saída. Ou até mesmo invólucros encapsulando o retorno bruto do método com dados adicionais para controles de erros. A mais comum era a lista de mensagens de erro.
Por um lado, sempre tive a clareza do propósito de angariar o máximo possível de informações para devolver ao usuário mais insumos para que a próxima tentativa tivesse mais possibilidade de sucesso. Por outro, também sempre tive o entendimento de que disparar uma exceção no primeiro erro não é a única forma de fazê-lo. Pensando nisso, preparei um guia sobre como essas exceções personalizadas podem contribuir para o gerenciamento de erros. Confira abaixo.
Contextos e métodos com objetivos
Uma vez que um método tem um objetivo, podendo incluir toda sorte de tratativas para que sua resposta seja entendida e o processo flua com sucesso, ele segue o conceito de segregação de contextos e responsabilidade única. Entretanto, quando falhas fatais são encontradas, o método já não atende sua funcionalidade, então é preciso coletar o máximo possível de informação e incluir na exceção. Além disso, buscar os detalhes de uma transação financeira pelo identificador da transação, por exemplo, pode retornar os detalhes ou não encontrar os dados, no caso de um identificador inválido ou inexistente.
Neste caso, não há motivos para uma camada de acesso a dados disparar uma exceção TransacaoFinanceiraNaoEncontradaException caso não encontre o dado. Mas e se encontrar mais de um registro onde deveria haver apenas um? O que está no banco de dados é o que está no banco de dados, não há como contestar que existe mais de um registro para aquele identificador. Se o método não sabe o que fazer neste caso, que dispare uma exceção MaisDeUmaTransacaoPorIdentificadorException.
Na sequência, a camada anterior, como uma camada de serviços, por exemplo, pode estar num contexto em que o registro deve sim existir, e ao identificar que o retorno foi nulo, disparar uma exceção TransacaoFinanceiraNaoEncontradaException.
Investigando outros incidentes
Já é possível notar pelos exemplos dados anteriormente que há bastante ganho semântico nessa abordagem. Considere também os ganhos da semântica na investigação de incidentes observando logs ou estatísticas em soluções de APM. Uma ocorrência de ArgumentNullException numa determinada rota é bem mais difícil de encontrar do que um PedidoNaoEncontradoException ou um ResponseVazioIntegracaoApiFornecedorException.
Ainda há um outro ganho possível, que são as formas de tratar essas exceções. Em aplicações ASP.Net, por exemplo, podemos adicionar um HttpContextMiddleware como um ExceptionMiddleware. Isso ajuda a lidar com essas exceções personalizadas e cuida de qualquer resposta HTTP diferente de sucesso, permitindo que as actions se preocupem apenas com o sucesso, como return Ok() ou return Created(). Eu costumo criar exceções HTTP base, delas eu derivo as exceções HTTP específicas, com mensagens e código de retorno, e dessas eu derivo as exceções da aplicação. Também crio um nível intermediário distinguindo entre erros do cliente ( 4xx ) e erros do servidor ( 5xx ).
Bora usar essas exceções?
Muito bem, agora você já viu na prática como todos as exceções personalizadas podem te ajudar, então é hora de colocar em prática. E se quiser fazer isso com outras pessoas, eu sempre recomendo a Comunidade da Impulso. Lá é o lugar perfeito para desenvolver junto com outros e outras devs, resolver problemas de código e criar networking. A gente se vê por lá! Qualquer coisa, vocês me encontram pelo @mfedatto.