Esse post é uma retaliação resposta ao post Não lance Exceptions em seu Domínio… Use Notifications! publicado pelo Wellington Nascimento. Para que você entenda o que vou falar aqui, ler o texto do Wellington é requisito fundamental, pois tudo que vou falar aqui se baseia na narrativa usada por ele.
Brincadeiras à parte, não há retaliação alguma, só uma reflexão sobre o que é “vendido” a respeito de notification pattern, algumas falácias naturais, e efeitos desses títulos e afirmações a respeito das exceptions. Que fique claro que nada tenho contra o padrão, nem contra quem publica artigos sobre o padrão, eu tenho algo contra o conteúdo dos artigos a favor de notification pattern, pois boa parte deles suprime o que quero tornar claro nesse texto .
Interrupção do fluxo de execução para cada inconsistência encontrada durante as verificações de regras de negócio;
O principal ponto, e que sou veemente contra, é que esse tipo de afirmação demoniza as exceptions. Afirmações assim fazem parecer boa prática abolirmos o uso de exceptions, essa é uma conclusão falaciosa que precisa ser melhor contextualizada, pois o padrão sugere que você o aplique para validações. Na prática não abolimos exceptions, reduzimos a quantidade destas e movemos de lugar, mas de fato não deixamos de lançar exceptions.
Seguindo a proposta de aplicar notification pattern proposta pelo Wellington, vemos que ele deixa de lançar exceptions no preenchimento da entidade, o que produz uma nova possibilidade: A possibilidade de existir uma instância inválida, ou inconsistente.
Levando em conta o exemplo dele, do Customer, infelizmente Customer ainda é anêmica pois o exemplo dele parou na explicação sobre notification pattern e ele não abordou um cenário de negócio, onde pudéssemos ver métodos no Customer. Portanto não podemos ver o que eu quero explicar pela perspectiva do código dele. Assim vou tomar a liberdade de copiar e escrever aqui no post.
O exemplo dele não possui os métodos Store e Checkout, tomei liberdade de adicioná-los para dar vida ao meu ponto de vista.
A pergunta que eu faço é: Nos métodos Store e Checkout, o que fazer se o estado do Customer for inválido?
Você tem uma validação prévia para garantir os requisitos da operação Store, se você está chamando o método store de uma entidade inválida, o fluxo precisa ser interrompido, pois quem programou o consumo desse método ignorou a verificação de estado. E na minha visão lançar essa exception, nesse caso, é OBRIGAÇÃO do método Store.
Não estou discutindo se a exception deve ser genérica, específica, isso é outro aspecto a ser pensado e depende de como você está desenhando sua solução. Mesclando com notification pattern, eu tenderia a usar exceptions genéricas, pois estas não precisam de tratamento específico, precisam apenas interromper o processamento pois algo não está consistente o suficiente para prosseguir, e na prática não é uma questão de validação (a validação está disponível sem lançar exceptions), é uma questão de bloqueio/trava de segurança/programação defensiva feito para causar uma interrupção de fluxo, pois é uma condição não permitida (que poderia ser evitada).
[pt-br] Preciso enfatizar aqui que não estou defendendo a eliminação de exceções em toda a sua base de código. As exceções são uma técnica muito útil para lidar com um comportamento excepcional e afastá-lo do fluxo principal da lógica. Essa refatoração é boa para ser usada somente quando o resultado sinalizado pela exceção não for realmente excepcional e, portanto, deve ser tratado por meio da lógica principal do programa. O exemplo que estou vendo aqui, validação, é um caso comum disso.
Textos como esse do Fowler são cuidadosos ao deixar esse disclaimer para sua audiência. Ele se preocupa em levar em conta o que equivocadamente poderia ser conclusão óbvia. Conclusão esta que vemos em diversos posts sobre Notification Pattern, conclusões que discuti, por exemplo, em um vídeo do Software em Contexto.
O ponto é que exeptions são necessárias para cenários não previstos e também para cenários não suportados, como nesse exemplo, ao tentar persistir um Customer inválido. Talvez no Checkout faça sentido também.
Trago aqui um exemplo simples, e perfeito para exemplificar uma abordagem, que na minha visão, é correta: System.IO.File.Delete
Nesse exemplo temos as exceptions possíveis, e são várias, quando tentamos excluir um arquivo. E se quiser identificar se o arquivo pode ser excluído, há métodos suficientes para isso, como o Exists entre outros. Mas ao realizar a operação, ao “Comandar” a execução do Delete, qualquer cenário adverso que não permita a realização da atividade irá retornar uma exceção. E, pra mim, está corretíssima essa abordagem.
Minha regra para exceptions é: Toda operação deve ser capaz de validar se seus pré-requisitos são atendidos para a execução de sua atividade. Requisitos não atendidos devem produzir exceções. Isso não impede de você aplicar notification pattern para evitar que essas exceções sejam lançadas, se o fizer bem feito, essas exceções só serão lançadas nos seus testes unitários. Esse viés é parte da programação defensiva.
Sim, quanto maior o reaproveitamento, maiores são as chances de algum erro no código produzir novos cenários de inconsistência. Caso surja um novo consumidor de seu método/classe que não leve algo em conta ou simplesmente tenha sido programado errado, é importante não permitir inconsistência.
Evitar que as exceptions sejam lançadas não significa que você vá eliminá-las de sua base de código, pois elas servem como trava de bloqueio para evitar corrupção!
Mesclar notification pattern com exceptions é uma boa pedida. Para cada operação você divide validação e operação. No Checkout teríamos 2 métodos:
Já o método Checkout() seria responsável por chamar o método ValidateForCheckout() para saber se suas pré-condições foram atendidas, e senão… lançar uma exception.
Já vi implementações de ValidateForCheckout() que recebe um parâmetro boolean informando se o resultado negativo lança ou não lança uma exception. Particularmente eu gosto, dessa abordagem também.
Não há discriminação a respeito do uso de Notification Pattern, é um padrão útil. Mas dizer que não devem ser lançadas exceptions, não é legal. Dizer que é deselegante, também não é legal. Diga: “não lance exceptions para validação” em vez de “não lance exceptions”. Tenha o cuidado de dizer quando usar exceptions.
Se realizadas pré-validações, é possível não lançar nenhuma exceção, mas isso nunca vai querer dizer que elas não devam existir, ou que abandonou-as em função do padrão. Não lançar exceptions pode lhe custar caro, principalmente se o nível de reaproveitamento de suas classes for alto.
Notification Pattern não é problema, mas os posts que o defendem pecam em não deixar claro que as exceptions não morrem. Eles produzem a sensação de que exceptions deveriam ser abolidas e isso nem de longe é uma verdade.
Um padrão comecei a estudar e aplicaria usando o Notification pattern junto com Exception seria de não deixar o objeto ser instanciado de forma inválida. No construtor ou usando um Fabrica chamaria a verificação “IsValid” e lançaria a InvalidArgumentException com as mensagens do ValidationResult
O pattern defende algo diferente. Com as tuplas de retorno de métodos no C# seria possível mesclar validação e entidade de retorno (se não me engano GO trabalha assim, posso estar errado, eu não tenho certeza disso, mas acho que ouvi dizerem isso).
Muito boa essa discussão sobre validações, eu estava lendo alguns artigos sobre validação e tinha me deparado com várias abordagens, essa pra mim foi a que fez mais sentido e até me esclareceu alguns pontos que eu estava em dúvida. Valeu pelo post.
Bacana. Acho que complementou bem o post do Wellington. Tenho usado specifications com Exceptions em um pequeno projeto. Tá interessante de usar por enquanto. 🙂
Boa Leandro, embora tenha feito a firula de dizer que era uma retaliação o post dele é apenas um dos exemplos que pecam nesses pequeninos detalhes, mas que fazem diferença, pois muita gente se baseia em conteúdos assim para formar opinião. Acaba virando um mega telefone-sem-fio.
Muito bom seu ponto Gago, tinha lido posto do Wellington anteriormente e tinha achado muito interessante agora com seu complemento, mudou minha visão, estava com pensamento radical sobre as exceptions, seria bacana mais exemplos desses com notificação/exceção, obrigado!!
O ponto central que me leva a essa reflexão é que exceptions são fundamentais para bloquear processamento indevido e inconsistente. Que pode ser fruto de entrada do usuário ou simplesmente uma falha de implementação.
Adicionar Notification Pattern tem muito potencial para sanar problemas com performance e usabilidade, no entanto como não entra na alçada de bloquear processamento indevido e inconsistente, é instantaneamente óbvio que o padrão não suprime a necessidade de exceptions.
Acredito que o empenho de notification pattern é uma escolha dos times, dos projetos, diferente do uso de exceções, que está relacionado à confiabilidade de um kernel.
Bom dia, em primeiro lugar quero deixar claro que acho discussões sobre tecnologia e desenvolvimento extremamente valiosas e de grande aprendizado para todos nós, e em resposta à sua “retaliação” acho que tenho direito de trazer alguns pontos.
Lendo seu post fiquei com o sentimento de ser acusado de fazer algo muito errado, de estar ensinando conceitos errados para a comunidade de forma proposital, mas posso te garantir que tudo que escrevo é fruto de muito estudo e experiências que tive em projetos reais que tive o prazer de participar nos últimos anos e que aplico em meu dia a dia, e de forma alguma tentaria vender algo errado ou que não acredito para a comunidade.
Não estou tentando endemonizar o uso de Exceptions, elas são reconhecidamente recursos valiosos para travar/bloquear a execução de código e informar um comportamento inesperado, um erro na aplicação, e eu não sou ninguém para dizer que elas devem ser abolidas.
Se você leu meu artigo e entendeu isso, talvez eu tenha realmente me expressado mal (acho que faltou um disclaimer, como o que o Fowler fez), mas longe disso, eu como qualquer outro desenvolvedor uso exceptions em meus sistemas, tanto pessoais como em projetos comerciais em que atuo.
Provavelmente uma simples mensagem sua me informando sobre o ponto em questão me faria re-avaliar o que escrevi e fazer as devidas correções em meu artigo, pois como eu disse, de forma alguma quero endemonizar o uso das exceptions e não havia percebido que meu texto tinha essa “intenção”, e é justamente para melhorar os textos e corrigí-los se necessário que eu peço no final de cada artigo que me enviem críticas e sugestões. Vou fazer a correção, e ainda assim obrigado por avisar, mesmo que com uma “retaliação”. #communityrules
1) Aprovei todos os comentários e aprovarei os demais, já que o texto pauta usa seu post como premissa de entendimento. Aprovarei todos os demais sem discriminação alguma.
A falta de alertas para quem lesse o post, tem potencial para concluir que sim, as afirmações que copiei só estão contextualizadas sob o aspecto de pertencerem a um domínio. Assim o trecho “O lançamento de exceptions no domínio traz alguns problemas:
Se uma mensagem em um comentário poderia produzir uma mudança, sim de fato, poderia. Se olhar bem o que escrevi em diversos momentos faço acenos claros e em momento algum eu lanço pedras no pattern.
A questão central é que seu texto se encaixa perfeitamente em um estereótipo de posts sobre Notification Pattern que levam o leitor rumo entendimento de que exceptions são ruins, onerosas e deselegantes independente do contexto. Embora não seja objetivo, é uma informação que se pode tirar, principalmente pela forma como algumas coisas foram escritas. São detalhes, mas produzem essa ideia.
communityrules é outro tópico (não tem a ver com esses textos, nem o meu, nem o seu) hahaha, isso é outro assunto que não tem nada a ver com esses posts.
Entendo seu ponto Luiz, e concordo com você que a falta de um disclaimer sobre as exceptions pode levar o leitor para um entendimento errôneo do que eu queria realmente dizer sobre usar exceptions no domínio, por isso disse que uma simples mensagem sua avisando sobre esse aspecto que eu não havia percebido, seria o suficiente para que eu melhorasse meu texto incluindo tal disclaimer e contextualizasse ainda mais o trecho específicio à que você se refere, evitando assim que futuros leitores caíssem nesse entendimento errado.
Se pela falta de um disclaimer meu texto caiu nesse estereótipo que você descreveu, sinto muito, falha minha, eu irei melhorar o texto e prestar mais atenção para que isso não volte à ocorrer no futuro, mas de forma alguma estou vendendo um conceito errado propositalmente.
Luiz, tirei um tempo no fim de semana e revi meu texto. Inclui o disclaimer que concordo que eu deveria ter colocado já na primeira versão do artigo, e também alterei outros pontos para ficar melhor contextualizado o que eu queria dizer. Se você tiver um tempo para refazer essa leitura e me dizer o que achou após as modificações eu ficaria grato.
Prosseguindo, em determinado momento você menciona que o uso do padrão deve ser contextualizado pois ele sugere que deve ser usado para validações, dando a entender que eu não mencionei o contexto onde as exceptions seriam substituídas por notificações. Bem, lendo meu próprio artigo acho que deixei bem claro que o pattern deve ser aplicado em validações de domínio.
“É muito comum encontrar nos sistemas corporativos o lançamento de Exceptions ao realizar validações de regras de negócio…”
“Essa entidade expõe as mensagens de erro das validações através da propriedade ValidationResult da classe base…”
“…após instanciar a entidade Customer ela irá armazenar o resultado de suas validações na propriedade ValidationResult”
“Perceba que interrompemos o fluxo de execução apenas uma vez, após todas a validações terem sido feitas…”
Outro ponto, a questão de as exceptions serem “deselegantes” quando aplicadas em validações é pura e simplesmente uma opinião minha como desenvolvedor de software e eu realmente acho isso. Sobre os outros pontos, sim elas param abruptamente a execução de código mostrando apenas a primeira falha de validação encontrada, e elas realmente não são legais com o processador.
Note, que no seu texto original você diz “O lançamento de exceptions no domínio traz alguns problemas:” … “São deselegantes.” e agora você diz “a questão de as exceptions serem deselegantes quando aplicadas em validações”.