C# - Tratando exceções no modelo de domínio


Hoje veremos como podemos tratar exceções no modelo de domínio.

Em artigos anteriores (veja as referências) eu já apresentei uma breve comparação entre o modelo anêmico e o modelo rico, e assim você sabe porque deve optar por usar, sempre que possível e pertinente, um modelo de domínio rico.

Como exemplo apresentamos o seguinte de modelo de domínio rico:

Nota: Devido a simplicidade do exemplo nosso domínio não possui nenhuma complexidade de negócio e assim não temos nenhum comportamento expresso no domínio

    public class Cliente
    {
        public int Id{ get; private set; }
        public string Nome { get; private set; }
        public string Endereco { get; private set; }     
        public Cliente(int id, string nome, string endereco)
        {
            if (id < 0)
                throw new InvalidOperationException();
            if (string.IsNullOrEmpty(nome) || string.IsNullOrEmpty(endereco))
                throw new InvalidOperationException();
            Id = id;
            Nome = nome:
            Endereco = endereco;
        }
    }

Aqui o cliente da classe somente pode atribuir valores para as propriedades via construtor e os valores estão sendo validados(de forma bem ingênua) o que garante mais consistência ao objeto gerado.

Obs: Se a classe for consumida por um código externo não será possível alterar o seu estado pois suas propriedades são somente leitura assim teríamos que criar um método para permitir isso.

Observe que na validação estamos lançando Exceptions e isso indica a ocorrência de algo anormal que ocorreu durante a execução do programa o que é bem diferente de erros que ocorrem na violação das regras de negócio.

Outro problema em lançar Exceptions é que isso tem um custo, e, por isso não devemos abusar no lançamento de exceções para controlar o fluxo da aplicação.  Assim é bom evitar lançar exceções nas validações.

Em um cenário normal a informação de dados inválidos para o Cliente é algo esperado e normal, e, neste caso não seria aconselhável lançar exceptions. Segundo Martin Fowler: 'If a failure is expected behavior, then you shouldn’t be using exceptions.'

Neste contexto Martin Fowler também definiu o Notification Pattern que pode ser usado para capturar mensagens das validações de domínio e levar essas mensagens para a camada de apresentação.

Nota: Ninguém aqui esta dizendo para você não lançar Exceptions, ok ! Leia o artigo Replacing Throwing Exceptions with Notification in Validations

Então como podemos tratar exceções no modelo de domínio ?

Para o nosso exemplo uma outra forma mais elegante seria criar uma classe para gerenciar as exceções que ocorrem no domínio.

Podemos assim criar a classe DomainExceptionValidation:

    public class DomainExceptionValidation : Exception
    {
        public DomainExceptionValidation(string error) : base(error)
        {
        }
        public static void When(bool hasError, string error)
        {
            if (hasError)
                throw new DomainExceptionValidation(error);
        }
    }

Que pode ser usada assim:

    public class Cliente
    {
        public int Id { get; private set; }
        public string Nome { get; private set; }
        public string Endereco { get; private set; }

        public Cliente(int id, string nome, string endereco)
        {
            DomainExceptionValidation.When(id < 0, "O Id não pode ser negativo.");
            DomainExceptionValidation.When(string.IsNullOrEmpty(nome), "O nome deve ser informado.");
            DomainExceptionValidation.When(string.IsNullOrEmpty(endereco), "O endereço deve ser informado.");
            Id = id;
            Nome = nome;
            Endereco = endereco;
        }
    }

É uma solução bem simples que apenas organiza o código.

A outra solução seria implementar o padrão Notification Pattern, e, isso pode ser feito de diversas formas.

Outra opção é usar o Flunt que é uma biblioteca criada pelo MVP André Baltieri- https://balta.io/blog/exception-vs-domain-notification e que pode ser encontra aqui : https://github.com/andrebaltieri/flunt

E estamos conversados...

"Visto como na sabedoria de Deus o mundo não conheceu a Deus pela sua sabedoria, aprouve a Deus salvar os crentes pela loucura da pregação.
Porque os judeus pedem sinal, e os gregos buscam sabedoria;
Mas nós pregamos a Cristo crucificado, que é escândalo para os judeus, e loucura para os gregos."
1 Coríntios 1:21-23

Referências:


José Carlos Macoratti