É uma questão interessante que parece surgir em uma variedade de formas.
Eu sou da opinião de que a melhor abordagem é permitir o conceito de um objeto que é um estado inválido.
A razão é que as regras de validação para um objeto geralmente não são definidas em pedra. Eles podem mudar com o tempo ou ser diferentes para diferentes operações. Assim, um Objeto que você criou, preencheu e persistiu algum tempo atrás, pode agora ser considerado inválido.
Se você tiver verificações de validação de setter ou constructor, então você tem um grande problema, pois seu aplicativo irá cometer erros quando você tentar recuperar essas entidades de seu banco de dados, ou reprocessar entradas antigas, etc.
Além disso, não acho que as regras de negócios incorporem a simples validação sim / não na maior parte. Se o seu domínio está vendendo bolos e você não entrega ao sul do rio, mas alguém lhe oferece um milhão de libras para fazê-lo. Então você faz uma exceção especial.
Se você estiver processando milhões de aplicativos e tiver regras rígidas sobre quais caracteres podem estar em um campo, provavelmente terá um processo para corrigir campos inválidos. Você não quer ser incapaz de aceitar um campo ruim, apenas segue um caminho diferente através do Domínio.
Assim, se no código você é tão rigoroso que os dados 'inválidos' podem simplesmente não existir porque o construtor lançaria uma exceção, você está fadado a ser frágil e a falhar em questões como "quantas pessoas preencheram o formulário errado "
Permitir os dados e falhar na operação. Dessa forma, você pode ajustar os dados ou regras para a operação e executá-la novamente.
exemplo:
public class Order
{
public string Id {get;set;}
public string Address {get;set;}
public void Deliver()
{
//check address is valid for delivery
if(String.IsNullOrWhiteSpace(this.Address))
{
throw new Exception("Address not supplied");
}
//delivery code
}
}
Então, aqui não conseguimos ou não desejamos enviar para endereços em branco. O objeto Ordem do domínio permite preencher um endereço em branco, mas lançará uma exceção se você tentar entregar esse pedido.
Um aplicativo, digamos, um trabalhador de fila processando pedidos de dados json armazenados em uma fila, encontrando uma ordem 'inválida':
{
"Address" :""
}
É capaz de criar o objeto Order, pois não há verificações de validação no construtor ou o setter para Address. No entanto, quando o método Deliver for chamado, ele lançará a exceção e o aplicativo poderá executar uma ação. eg
public class QueueWorker
{
public void ProcessOrder(Order o)
{
try
{
o.Deliver();
}
catch(Exception ex)
{
Logger.Log("unable to deliver order:${o.Id} error:${ex.Message}");
MoveOrderToErrorQueue(o);
}
}
}
O Aplicativo ainda pode trabalhar com o Pedido Inválido, movendo-o para a fila de erros, acessando seu ID, relatando erros, etc. Mas a Operação Entregar contém a lógica do Domínio de como você deseja manipular Entrega para endereços em branco. p>
Se a lógica do domínio mais tarde mudar com outros requisitos:
public class Order
{
public string Id {get;set;}
public string Address {get;set;}
public void Deliver()
{
//check address is valid for delivery
if(String.IsNullOrWhiteSpace(this.Address))
{
throw new Exception("Address not supplied");
}
if(Address.Contains("UK"))
{
throw new Exception("UK orders not allowed!");
}
//delivery code
}
}
Você ainda pode processar pedidos na fila gerados quando os endereços do Reino Unido foram permitidos e obter o resultado esperado.
É interessante comparar minha resposta com a de @VoiceOfUnReason.
Eu usei uma string Address para simplificar e também porque não há uma definição absoluta do que é um endereço. Mas se tivermos uma definição mais absoluta, digamos que seu depósito sempre tenha uma moeda. É absurdo até falar sobre um depósito sem moeda.
Nesse caso, sim, você pode definir um novo tipo de valor que simplesmente não pode existir, a menos que você tenha especificado a moeda e os possíveis erros em seu código simplesmente não serão possíveis.
Mas você tem que ter certeza de que é uma coisa fundamental. Caso contrário, você está pedindo problemas depois!