Notification Pattern – Estão te vendendo um conceito errado
Publicado em: domingo, 28 de abr de 2019

Notification Pattern prevê uma forma permissiva que troca exceptions por notificações em um determinado contexto, no entanto embora a maioria das publicações a respeito falem em não lançar exceptions, veremos que isso não é bem assim.

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 .

No texto original Wellington diz:

O lançamento de exceptions no domínio traz alguns problemas:

Interrupção do fluxo de execução para cada inconsistência encontrada durante as verificações de regras de negócio;
São onerosas para o processador;
São deselegantes.

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.

    public class Customer : Entity
    {
        public string Name { get; }
        public string Email { get; }

        public Customer(string name, string email)
        {
            Id = Guid.NewGuid();
            Name = name;
            Email = email;

            Validate(this, new CustomerValidator());
        }

        public void Store()
        {
           ...
        }

        public void Checkout(Cart cart, Payment payment, Address billingAddress, Address deliveryAddress)
        {
            ...
        }
    }

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?

  1. Ignorar a ação?
  2. Retornar nulo?
  3. Usar notification pattern novamente?
  4. Lançar exceptions?

As opções 1, 2 e 3, para mim, estão categoricamente erradas.

Meus argumentos:

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.

Seria assim:

    public class Customer : Entity
    {
        public string Name { get; }
        public string Email { get; }

        public Customer(string name, string email)
        {
            Id = Guid.NewGuid();
            Name = name;
            Email = email;

            Validate(this, new CustomerValidator());
        }

        public void Store()
        {
           if(this.Invalid) throw new InvalidEntityException("Texto aqui");

           ...
        }

        public void Checkout(Cart cart, Payment payment, Address billingAddress, Address deliveryAddress)
        {
            ...
        }
    }

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).

Marting Fowler deixa claro isso no post Replacing Throwing Exceptions with Notification in Validations.

No tópico When to use this refactoring ele deixa claro:

[en-us] I need to stress here, that I’m not advocating getting rid of exceptions throughout your code base. Exceptions are a very useful technique for handling exceptional behavior and getting it away from the main flow of logic. This refactoring is a good one to use only when the outcome signaled by the exception isn’t really exceptional, and thus should be handled through the main logic of the program. The example I’m looking at here, validation, is a common case of that.

[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.

Ele complementa com um trecho do livro Programador Pragmático (leitura recomendada).

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


Documentação de 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.

Regra geral para exceptions

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.

Será que faz sentido uma exception que só é lançada no teste unitário?

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:

ValidateForCheckout()

Responsável pela validação, respondendo notificações.

Checkout()

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.

Conclusão

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.

O exemplo do System.IO.File.Delete é fantástico para tornar claro esse ponto de vista.

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.

#TretaFree

Vale lembrar que eu entrei em contato com o Wellington pedindo para citar o post dele.

Por falar em treta, já está na hora de falarmos de community rules, né!

Gostou desse post? Conta aí? Comente!

Espero que eu tenha conseguido ser claro na minha visão sobre o tema.

Luiz Carlos Faria

Mensagem do Autor

Espero que goste desse post. Não deixe de comentar e falar o que achou. 

Se acha que esse post pode ajudar alguém que você conheça, compartilhe!

 

Eventos passados

agosto 2020
julho 2020
Nenhum evento encontrado!
Carregar Mais

Publicidade

Assine

34 Comentários

  1. Paulo Correia

    Parabéns pelo post concordo totalmente com seu ponto de vista.
    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

    Responder
    • Luiz Carlos Faria

      Obrigado Paulo!
      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).

      Responder
  2. Tiago Ávila

    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.

    Responder
    • Luiz Carlos Faria

      Muito obrigado pelo feedback Tiago, que bom que ajudou!

      Responder
  3. Leandro Laia

    Bacana. Acho que complementou bem o post do Wellington. Tenho usado specifications com Exceptions em um pequeno projeto. Tá interessante de usar por enquanto. 🙂

    Responder
    • Luiz Carlos Faria

      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.

      Responder
  4. Vinícius Mamoré Caldeira de Oliveira

    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!!

    Responder
    • Luiz Carlos Faria

      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.

      Responder
  5. Wellington Nascimento

    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

    Responder
    • Luiz Carlos Faria

      Wellington Nascimento,
      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.

      2) Agradeço ter separado um tempinho para voltarmos ao assunto, isso é muito útil para a discussão.

      Responder
    • Luiz Carlos Faria

      Sobre ser acusado de estar ensinando conceitos errados para a comunidade:
      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:

      Interrupção do fluxo de execução para cada inconsistência encontrada durante as verificações de regras de negócio;
      São onerosas para o processador;
      São deselegantes.”
      Sem o devido disclaimer demoniza exceptions.

      E sim, um disclaimer poderia ter resolvido facilmente quase toda a questão, eu concordo contigo.

      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.

      Responder
      • Wellington Nascimento

        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.

        Responder
        • Wellington Nascimento

          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.

          Responder
  6. Wellington Nascimento

    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…”

    “Para capturar as mensagens das validações de domínio podemos usar o pattern Notification…”

    “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.

    Responder
    • Luiz Carlos Faria

      O ponto em questão é a forma de escrever.

      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”.

      Enquanto a primeira pode levar a um entendimento sobre o que as exceções são, o segundo traz o contexto. Por mais que o post fosse sobre notification, todo post é sobre um contexto determinado. E todo post traz consigo definições que são contextuais e definições que não são contextuais, é assim que nos comunicamos ao atribuir adjetivos aos padrões, tecnologias, comportamentos .

      Responder
  7. Wellington Nascimento

    Sobre o pattern em sí, no artigo do Martin Fowler que usei como referência, ele indica quando o pattern deve ser usado, e nesse caso, substituindo o uso de exceptions pois elas mostram apenas o primeiro erro de validação já que travam o fluxo de execução já na primeira falha de validação, os demais erros serão descobertos apenas nas chamadas seguintes. No mesmo artigo, ele mostra um exemplo de código implementando o pattern em uma aplicação Windows Forms com C#, sem fazer uso de exceptions, então na prática, elas nem foram usadas ao aplicar o pattern, da mesma forma como eu fiz.

    “The most obvious alternative to using Notification is for the domain to use exception handling to indicate errors. Such an approach has the domain throw an exception if a validation check fails. The problem with this is that it only indicates the first validation error. It’s usually more helpful to show every validation error, particular if validation requires a round trip to a remote domain layer.”

    O artigo do Fowler que você usou como referência, é um hands-on de como é possível substituir exceptions por notifications ao fazer validações em um código já existente, um refactoring, e no final do artigo ele entrega um código onde as exceptions foram substituídas por notifications, como eu fiz. Claro, ele como um guru de software e velho de comunidade sabe que deve deixar bem claro que não está abolindo o uso de exceptions e por isso o disclaimer no começo do artigo.

    “This article is about replacing exceptions for notification in the context of validating raw input. You may also find this technique useful in other situations where a notification is a better choice than throwing an exception, but I’m focusing on the validation case here, as it is a common one.”

    Ao final, ele deixa claro que o objetivo à médio prazo é eliminar completamente o uso de exceptions em que se possa esperar falhas de validações.

    “… the medium-term target should be to eliminate the use of exceptions in any circumstance where we might expect validation failures.”

    Ainda sobre um dos exemplos que foram usados, em que você citou o System.IO.File.Delete, concordo 100% que ele está correto em lançar exceptions, ele é um método de infraestrutura do framework e como tal não caberia o uso de notification pattern, já que o padrão prega que deve ser usado em validações de domínio, não infraestrutura.

    “An object that collects together information about errors and other information in the domain layer and communicates it to the presentation.”

    Responder
    • Luiz Carlos Faria

      Nesse primeiro ponto, onde você compara teu exemplo, eu senti falta do que seria a implementação de uma operação que necessitasse do Customer válido para prosseguir. Acho que ter uma essa explicação, seria esclarecedora.

      Sobre o System.IO.File.Delete, pela perspectiva dele, ele é o domínio. Ele pode ser infraestrutura no seu contexto, mas para ele, então não faz a menor diferença. Essas demandas por assegurar pré-condições e pós-condições não são exclusividade da infraestrutura.

      Responder
  8. Wellington Nascimento

    Sobre o exemplo que usei, a entidade Customer não tem métodos para enriquecer ainda mais o exemplo pois não achei necessário já que eu conseguia explicar o conceito do pattern apenas através de validações no construtor, o fluxo não é travado nesse momento, e também não seria nos métodos de alteração de estado caso existissem, pois ao meu ver não é responsabilidade da entidade travar a execução do código, ela deve sim se validar e sinalizar que está inválida, com todas as validações possíveis feitas nesse momento, sem travar a execução. O consumidor, no meu exemplo um command handler, deve verificar se existem erros de validação (integridade da entidade) antes de persistí-la no banco de dados por exemplo, o mesmo comportamento deveria ser feito em alguma abordagem com um Application Service, que na prática orquestram a execução do request e podem bloquear a execução com um simples “return” devolvendo as notificações para o client.

    O desenvolvedor deve ser apto a verificar o estado da entidade antes de persistí-la, da mesma forma que deve verificar se um objeto retornado por algum método está nulo antes de fazer alguma operação com ele. Para ambos os casos, testes de unidade pegariam os casos onde o desenvolvedor tivesse esquecido de verificar a integridade dos objetos.

    Concordo que a falta de um bloqueio do fluxo de forma nativa no pattern pode causar algum problema em potencial, se não usado corretamente. Ao usar notifications pattern, inevitavelmente será necessário verificar se existem notificações antes de prosseguir com o processamento e assim evitar esses problemas. Disparar uma exception no final para bloquear o fluxo após as validações é algo que eu nunca havia visto ser usado com notification pattern, funciona nesse sentido, mas não é a única forma de se ter esse efeito, além disso, ainda é necessário verificar as notificações.

    Responder
    • Luiz Carlos Faria

      Eu entendi perfeitamente onde você parou, e citei isso. O ponto-chave é que, como citei em outra resposta de um comentário teu, ter um método qualquer que dependesse do Customer estar válido poderia mudar totalmente a mensagem que seu post entrega.

      Responder
      • Wellington Nascimento

        No exemplo mostrei um cenário hipotético de criação de um Customer com nome e email, não haveriam mais métodos de Customer à serem chamados alí. A criação da instância do Customer se resolveu no construtor e daí surge o estado válido ou inválido dele, que é verificado pela classe que está orquestrando sua execução.

        No cenário onde o Customer já esteja criado e uma operação é feita sobre ele, por exemplo um “ChangeEmail” ou qualquer outro, o Customer já válido que foi criado préviamente seria recuperado de alguma fonte de dados, e então a operação seria aplicada sobre ele. Não existe nesse caso uma operação feita sobre um Customer inválido. Ele pode ficar inválido devido à operação, o email pode estar errado, e uma notificação de email inválido seria criada.

        Não vejo um cenário onde um Customer fosse criado e no mesmo request seu email alterado. Não estou dizendo que isso é impossível de acontecer, mas de qualquer modo ambos os casos poderiam deixar a entidade em um estado inválido, que deveria ser verificado pela classe que está orquestrando a execução.

        Responder
    • Luiz Carlos Faria

      Sobre Notification Pattern + Exceptions, eu tenho uma resposta simples que vai de encontro com o que disse.

      Não existe validação, produzida por notification pattern que em outra parte código não deva produzir direta ou indiretamente uma exception.

      Isso quer dizer que se notification pattern for aplicado em escala, para 100% dos cenários e ainda sim eu tiver para todos estes cenários, suas devidas exceptions prontas para serem lançadas, de fato estas exceptions nunca seriam lançadas de fato, somente em seus testes unitários.

      Não importa se estamos falando de domínio rico ou domínio anêmico ou de serviços, se há um pré-requisito não atendido à chamada de um método, esse precisa ser validado e se não atendido, exceptions precisam ser lançadas. Mas se você colocar o Notification Pattern na frente quebrando a validação em notificação + execução do core, você consegue nunca lançar sequer 1 exception, o que não diz que elas serão removidas do código, provavelmente são consolidadas apenas.

      Responder
      • Wellington Nascimento

        O que você está me dizendo é que obrigatóriamente uma validação de domínio que produza uma notificação, deve produzir uma exception.

        As validações podem produzir resultados válidos e inválidos, sendo que no caso de um resultado inválido uma notificação deve ser criada informando qual foi a falha. Ambos os cenários são previsíveis e perfeitamente esperados.

        A própria definição de Exception diz que elas são excepcionais, para cenários inesperados, o que acontece é que é um recurso simples de usar e funciona para parar o processamento, levando qualquer mensagem para cima na stack.

        Como eu disse em outro comentário, usar notification pattern implica que você deverá verificar se existem notificações antes de prosseguir com o processamento, se existir, um return para o client com o resultado das validações já basta e nenhuma exception precisa ser lançada.

        No caso o que são consolidadas são as mensagens produzidas pelas validações.

        Responder
        • Luiz Carlos Faria

          O caso exceptional, é uma condição exigida ter passado para uma etapa em que não deveria ter passado.

          Vamos falar sobre um endereço inválido. Um endereço inválido é um problema que poderia ser resolvido com notification pattern durante seu cadastro. Em um cenário de um e-commerce, já durante o processo de dispatch da mercadoria, deveria lançar uma exception.

          O fato de você conhecer o problema não faz ele deixar de ser excepcional. Até porque seguindo essa linha de raciocínio não seria possível lançar exception alguma, pois uma vez sendo conhecido, deixaria de ser excepcional.

          Responder
        • Luiz Carlos Faria

          Não é possível vislumbrar sequer um cenário onde a mesma validação que retornou uma Notification não deva lançar uma exceção EM OUTRO MOMENTO do fluxo.

          Se há uma notification, há uma validação, se há uma validação então:

          Então ela é uma pré-condição para algo. Se uma pré-condição não é atendida uma exception precisa ser lançada quando não atendida.

          Já que não ficou claro, e pelo que vejo não está claro, vou separar um tempo amanhã a escrever sobre isso.

          Responder
          • Wellington Nascimento

            Acho que as coisas estão se misturando aqui.. rs

            Vamos lá, uma Exception de um jeito ou de outro é uma forma de notificar que algo deu errado, diante de uma validação para uma pré-condição.

            O pattern de notificações é uma forma de levar uma mensagem da camada de domínio para a camada de apresentação, e com isso temos a possibilidade de acumular essas mensagens, sendo que ele normalmente é aplicado para validações de domínio. Não é possível acumular os erros de validação do domínio se eles lançarem exceptions, e é ai que as exceptions são deixadas de serem usadas, apenas nesse cenário. Ao invés de lançar excpetions nas validações de domínio você acumula as mensagens em uma lista de strings por exemplo, e essa lista de strings é retornada para a camada de apresentação.

            O fato de lançar uma exceção posterior para uma validação que gerou notificação é simplesmente pelo fato de ter que bloquear o fluxo de execução após as validações não serem satisfeitas, e esse uso de exception também não é necessário se você simplesmente fizer um return.

            Eu concluo com isso, que o grande fato de lançar uma exception após as validações gerarem notificações é pura e simplesmente questão de gosto. Você pode lançar a exception para interromper o fluxo, ou usar return.

            Responder
            • Luiz Carlos Faria

              Se possível, me apresente somente 1 cenário (preferencialmente ligado a um negócio qualquer, hipotético ou real) em que você teria uma notificação em que em outro momento não deveria ter uma exceção.

  9. MAICON CARLOS PEREIRA

    Olá, eu tenho optado por um misto dos pontos de vistas dos dois. E apresento aqui como tenho adotado para ver as suas considerações. Gosto da forma fluente de escritas das regras como do framework Flunt e também do fato de ter um conjunto de falhas e não só o primeiro erro. Porém também não gosto da idéia de permitir que a entidade esteja em um estado inválido. Assim, faço uso (em projeto de estudo) do Flunt, porém lançando exceções no domínio. Veja como ficou e critica aí…kk https://github.com/maiconcp/viperex/blob/master/Anuncios/Viper.Anuncios.Domain/Entities/Anuncio.cs

    Responder
    • Wellington Nascimento

      Olá Maicon, sua implementação ficou bem interessante, criando um método de extensão ao Flunt para lançar uma DomainException ao final das validações com as mensagens acumuladas, e vai de encontro com o que o Luiz disse, de mudar a exception de lugar, lançando no final. Funciona bem para interromper o fluxo de execução dado o estado inválido das validações.

      Ao usar o Flunt, herdando a entidade da clase Notifiable que ele possui, implica que ela poderá ter dois estados (válido e inválido), no exemplo que usei em meu artigo segui essa abordagem, porém usando FluentValidation sem uma classe Notifiable, apenas olhando o resultado das validações para determinar o estado.

      Como você mencionou, no seu caso foi uma questão de preferência não optar por ter esse estado na entidade, e por isso o lançamento da exception em momento posterior se faz necessário, nesse caso você não poderia ter uma verificação de estado em uma Application Service por exemplo. No meu exemplo, como posso ter um estado válido ou inválido na entidade, obrigatóriamente tenho que verificar seu estado e então faço um simples “return” para interromper o fluxo de execução na classe que está orquestrando tudo, e ai a exception não foi necessária.

      Mas pelo que percebi nessa discussão, e que abordei na segunda versão do meu artigo, que isso é uma questão de preferênca lançar ou não a exception no final, além disso acho que cabe avaliar cada caso e ver se vale a pena lançar a exception. Para a maioria das aplicações o lançamento de exception vai resolver bem o problema.

      Mas, a questão que me vem na cabeça sempre que falamos disso, e que observo ao ler sobre boas práticas ao uso de Exceptions, é que conceitualmente elas não deveriam ser usadas para comportamentos previstos ou controle de fluxo. Então fica a questão, devemos ou não usá-las nas validações?… já que elas fazem parte de um comportamento esperado e de certa forma são uma condição para controle de fluxo.

      Um aspecto que o Martin Fowler aborda em seu artigo original, é que uma alternativa seria usar eventos para notificar as falhas de validação sem fazer o uso de exceptions, mas não cheguei a ver um exemplo com essa abordagem ainda. Talvez eu tire um tempo para testar isso.

      Responder
      • MAICON CARLOS PEREIRA

        Valeu Obrigado pelo Feedback, optamos por ter a entidade sempre válida, sem utilizar a herança do Notifiable, mas tendo os beneficios das validações concatenadas. A DomainException concatena as mensagens mas também tem uma lista interna das notificações. Na nossa camada de API, temos um ExceptionFilter que retorna um bad request com a lista destas notificações. Logo, a exception é lançada da camada de Domain e atravessa a Application. A questão de ser uma exception genérica pode não agradar a todos, mas queria compartilhar essa abordagem com vocês.

        Responder
      • Luiz Carlos Faria

        Pois é eu senti falta de um exemplo que demonstrasse a notificação retornando para uma UI ou algo do gênero.

        Entendo o propósito de notification e sim, concordo que ele traga para o projeto muitos benefícios, simultaneamente concordo com a abordagem sobre exceptions, só queria ver um exemplo em que simultaneamente víssemos exceptions e notifications quase que lado-lado.

        Responder
      • Luiz Carlos Faria

        Wellington, acabei só vendo o comentário agora. Perdão.

        Não vi a nova versão do post, vou dar uma olhada.

        Sobre a questão das exceções, por mais singela que seja a validação, e sejamos práticos, vamos para o exemplo de que um CPF precisa ser válido, por exemplo, em um contexto de e-commerce.

        No contexto do cadastro de usuário ou no fluxo em que ele está cadastrando aquilo, podemos tratar com notificações, sem o menor problema.

        Mas partindo de um pré-suposto hipotético de que não poderia emitir uma nota fiscal para um CPF inválido, no momento de realizar a tarefa de emitir essa nota, deveria lançar uma exception. O que não impede de haver uma outra notification lá no checkout, antes de fechar a compra ou efetuar o pagamento.

        Como disse no texto, não importa onde, o que em um lugar for uma notificação, deverá ser uma exception em outro, e não importa se estamos falando de preenchimento de campos, ou de consistência de CPF’s.

        O que pode eventualmente não ter exceções: Validações que não são impeditivas, e isso é comum no ciclo de evolução onde é o cliente quem mantém um cadastro.
        Vamos supor que RG ou Identidade não seja um campo obrigatório no passado, mas passou a ser agora. É um caso em que você tem novas notificações, e que só vai poder lançar exceções sobre o tema quando 100% da base estiver preenchida (utópico).

        Estou alucinando temas e e exemplos para tentar expressar os cenários.

        Responder
  10. Silvair Leite Soares

    Imagine o usuário tentando emitir uma nota fiscal em um sistema qualquer:

    1 – Este usuário preenche um formulário gigante, com digamos, 50^ campos;
    2 – O usuário clica no botão “Emitir NF-e”;
    3 – A requisição é enviada ao backend que então, inicia o processo de validação da entidade “NFe”;
    4 – No 5º campo é encontrado um problema;
    5 – O sistema lança uma exception que devolve o fluxo para o frontend, informando ao usuário que o 5º campo tem um problema;

    Porém, existe uma dezena de outros problemas a partir do 20º campo. Mas o usuário ainda não sabe disso, pois o fluxo do backend foi interrompido pela exception no 5º campo da NFe.

    Bom, o que quero dizer com esta pequeno exemplo, é que me parece que utilizar um modelo híbrido, talvez fosse a melhor alternativa, ou seja, devolvo notificações para validações simples (Fail Fast Validations) ou de domínio, onde geralmente tempos quase total controle sobre o contexto, e lanço exceções em situações onde o contexto da aplicação fica fora do escopo da minha aplicação, exemplos: Acesso a APIs de terceiros, acesso a banco de dados, acesso ao sistema de arquivos, etc.

    Nenhum dos dois modelos invalida o outro. Mas se auto complementam.

    Responder
    • Luiz Carlos Faria

      O texto trata da demonização das exceptions, é sobre a visão deturpada passada sobre alguns textos, especificamente nos textos dobre Notification Pattern.

      Você aponta para cenários onde só alguma validação só é feita com notification. É isso mesmo?

      Responder
      • Silvair Leite Soares

        Olá, Luiz Carlos Faria. Também sou contra a demonização das exceptions. As utilizo com muita frequência em meus sistemas. Como disse, apenas em situações onde não tenho como garantir o controle da operação.

        E quanto à sua pergunta, sim, existem cenários em que utilizo apenas notificações para interromper o fluxo da operação. Por exemplo, nas validações superficiais, aquelas que não exigem tantos recurso$ de hardware para sua execução.

        Geralmente utilizo um modelo semelhante a isso (em um software web):

        1 – Frontend -> Validações em frontend, utilizando javascript;
        2 – Backend -> Basicamente uma repetição das validações do front end. Aqui utilizo notificações, agrupando todas as falhas de preenchimento (notifications) e devolvo esta lista para o frontend;
        3 – Backend -> Validações mais custosas, envolvendo acesso a banco de dados ou quaisquer outros recursos, os quais tenho pouco ou nenhum controle.

        Este modelo, tem se mostrado muito viável e econômico, pois o passo 1 evita requisições desnecessárias, enquanto o passo 2 evita o consumo de recursos de hardware do servidor (validações de nível 3).

        Por exemplo, imagine que você está construindo uma API REST para emissão da NF-e. Esta API será utilizada para outros sistemas para emissão da nota fiscal eletrônica. O fluxo principal desta API seria, basicamente:

        1 – Recebo uma requisição com um JSON contendo os dados da NF-e;
        2 – Valido os dados do JSON;
        3 – Transformo-o em um XML válido para a NF-e;
        4 – Transmito este XML para a SEFAZ de destino (processo assíncrono);
        5 – Busco o retorno do processamento na SEFAZ com o protocolo devolvido pela própria SEFAZ no passo 4;
        6 – Utilizando, por exemplo, um webhook, notifico o consumidor da minha API sobe o final do processamento da NFe por parte da SEFAZ.

        No exemplo a cima, eu utilizaria:
        – Notificações para o passo 2;
        – Exceptions para os passos 4, 5 e 6.

        Responder

Enviar um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Lives

Fique de olho nas lives

Fique de olho nas lives no meu canal do Youtube, no Canal .NET e nos Grupos do Facebook e Instagram.

Aceleradores

Existem diversas formas de viabilizar o suporte ao teu projeto. Seja com os treinamentos, consultoria, mentorias em grupo.

Agenda & Eventos

agosto

setembro 2020

outubro
DOM
SEG
TER
QUA
QUI
SEX
SAB
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
Eventos para setembro

1st

Sem Eventos
Eventos para setembro

2nd

Sem Eventos
Eventos para setembro

3rd

Sem Eventos
Eventos para setembro

4th

Sem Eventos
Eventos para setembro

5th

Sem Eventos
Eventos para setembro

6th

Sem Eventos
Eventos para setembro

7th

Sem Eventos
Eventos para setembro

8th

Sem Eventos
Eventos para setembro

9th

Sem Eventos
Eventos para setembro

10th

Sem Eventos
Eventos para setembro

11th

Sem Eventos
Eventos para setembro

12th

Sem Eventos
Eventos para setembro

13th

Sem Eventos
Eventos para setembro

14th

Sem Eventos
Eventos para setembro

15th

Sem Eventos
Eventos para setembro

16th

Sem Eventos
Eventos para setembro

17th

Sem Eventos
Eventos para setembro

18th

Sem Eventos
Eventos para setembro

19th

Sem Eventos
Eventos para setembro

20th

Sem Eventos
Eventos para setembro

21st

Sem Eventos
Eventos para setembro

22nd

Sem Eventos
Eventos para setembro

23rd

Sem Eventos
Eventos para setembro

24th

Sem Eventos
Eventos para setembro

25th

Sem Eventos
Eventos para setembro

26th

Sem Eventos
Eventos para setembro

27th

Sem Eventos
Eventos para setembro

28th

Sem Eventos
Eventos para setembro

29th

Sem Eventos
Eventos para setembro

30th

Sem Eventos
Share This