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: