Representando regras de negócios com exceções

16

Eu sei que é caro, mas (IMO) acredito que é uma prática muito boa. Estou falando de regras como, digamos, você não pode salvar uma fatura se você não for um vendedor ... então, nesse caso, lançará uma exceção dizendo 'você não está autorizado' ou algo assim ...

Outra abordagem seria ter objetos com status ou algo assim

Existe alguma outra abordagem? Como você se sente com isso?

sebagomez
fonte

Respostas:

15

Se você quer dizer representar verificações de regras de negócios individuais com exceções, não acho que seja uma boa ideia. Muitas vezes, é necessário relatar mais de uma condição com falha e não parar na primeira.

Por outro lado, acredito que verificar todas as regras e depois lançar uma exceção com o resumo é uma boa prática.

matiash
fonte
1
É exatamente assim que eu manejo isso em meus aplicativos. Usar FluentValidation para facilitar minha vida e o método ValidateAndThrow salva meu dia.
Matteo Mosca
Em um aplicativo de interface do usuário, concordo completamente, mas em um aplicativo da camada de serviço eu geraria uma exceção se fosse aprovado em um objeto que não está em conformidade com as regras de negócios. Se você estiver usando os dois em combinação, às vezes você adicionará uma função de validação ou um erro complexo, como matiasha descreve na última frase.
Bill
8
Lance uma exceção específica do domínio . Isso permite que você se separe entre os nossos e os não-nossos mais alto.
@ Thorbjørn Ravn Andersen +1 "domínio específico" grande adição
Justin Ohms
1
@JamesPoulson, pode ser suficiente criar JamesPoulsonExceptioncomo uma subclasse de RuntimeExceptione, em seguida, fornecer a exceção original como o cause. Você pode simplesmente dizer if (exception instanceof JamesPoulsonException)... para diferenciar. Use o getCause()método para alcançar a exceção original.
9

No exemplo que você nos deu, acho que levantar uma exceção é uma má ideia. Se você sabe que o usuário não está autorizado antes de começar a trabalhar e você ainda permite que ele execute alguma função e depois bate com uma mensagem APÓS que eles já concluíram a tarefa, isso é apenas um projeto ruim.

Usar exceções para aplicar regras de negócios não é um bom design.

Walter
fonte
O que eu entendo do que você disse é que você não concorda com a interface do usuário. Eu estou bem com isso. Concordo com você que uma interface do usuário muito mais amigável não permitirá que você tente atualizar algo que não é permitido. Mas isso não significa que a verificação de validação e exceção no núcleo de negócios seja desnecessária. Pelo contrário, acredito que eles são uma obrigação. Você precisa deles para garantir que uma interface do usuário mal programada não permita um uso indevido do BL.
Fede
8
@Fede: é uma questão de separação de preocupações. A interface do usuário é responsável por reunir as intenções do usuário e informar os comentários da camada de negócios. O trabalho da camada de negócios é analisar as informações coletadas pela interface do usuário, analisá-las e reportar de volta à interface do usuário e / ou solicitar que a camada de dados persista alguns dados. Só deve haver um acoplamento frouxo entre essas camadas. As exceções são uma abordagem pobre e pobre ao acoplamento flexível. Isso não significa apenas compartilhar classes reais entre as camadas, mas também invoca um mecanismo destinado a lidar com falhas inesperadas.
Adam Crossland
@ Adam - Bem dito e exatamente no ponto.
29310 Walter
1
@ Adam - Não siga você com os problemas de separação de preocupações. Não espero que ninguém na interface do usuário lide com uma CustomerNameInLowercaseException (por favor, também espero que essa exceção não exista!). Você pode apenas manipular uma ValidationException genérica. Além disso, estou 100% com você que a interface do usuário deve coletar apenas informações e tudo mais. O que eu disse antes é que, não importa o que você faça na interface do usuário, o BL não deve assumir que todas as entradas estão ok. É apenas uma programação defensiva.
Fede
@Fede: o trabalho da Camada de Negócios é garantir que todas as entradas estejam OK. Se a interface do usuário estiver fazendo isso, está implementando a lógica de negócios. Agora, se é o caso de que um campo de entrada é restrita a dígitos eo BL vê caracteres alfa, por todos os meios lançar uma exceção . Sempre que um componente recebe entradas inválidas de outro componente, lançar uma exceção é lógico e correto. A interface foi quebrada e o sistema está quebrado. No entanto, validar entradas é muito diferente de executar a lógica de negócios. Duas coisas muito diferentes.
Adam Crossland
5

Não vejo qual o valor que gera uma exceção na criação de uma boa lógica de negócios. Existem dezenas de abordagens para lidar com a lógica de negócios que não envolvem o uso de um sistema destinado a tratar condições inesperadas na operação de um sistema.

Espera- se que, na lógica de negócios, as condições não sejam atendidas; esse é o motivo de tê-lo em primeiro lugar, e você não deseja usar o mesmo mecanismo que lida com falhas inesperadas de E / S, erros de falta de memória e referências nulas. Essas são falhas do sistema, mas detectar condições comerciais não atendidas é uma operação bem-sucedida do sistema.

Além disso, é um sistema que está maduro para conseqüências não intencionais. Como uma exceção precisa ser capturada em algum momento, você corre o risco de que uma nova exceção de regra de negócios seja capturada em algum lugar que não seja intencional ou você pode ter o código que procura regras de negócios que capturam exceções que não são realmente destinadas por isso. Sim, essas condições podem ser explicadas com boas práticas de codificação, mas em qualquer sistema não trivial no qual mais de um desenvolvedor esteja trabalhando, erros ocorrerão e você só precisa esperar que eles não sejam erros dispendiosos.

Adam Crossland
fonte
1
Concordo que exceções são chamadas exceções porque não se espera que aconteçam. Por outro lado, impor regras de negócios em sua camada de serviço com uma exceção não é necessariamente errado. Você não espera que seja usado, espera que o cliente invoque apenas operações e envie apenas dados que atendam a condições válidas; mas você deseja criar uma camada de proteção porque sabe que também podem ocorrer erros na codificação do cliente. É como usar restrições de chave estrangeira em um banco de dados: você espera que as pessoas insiram e atualizem os dados corretamente, mas sabem que podem falhar devido a um erro de programação.
Jeremy
2

expressar regras de negócios é uma coisa, implementá-las é outra

pense na experiência do usuário; se o usuário não é um vendedor, por isso dar-lhes um botão que diz 'criar factura' em tudo ?

Steven A. Lowe
fonte
1
Só porque você não aperta um botão, isso não significa que eles não enviarão a mensagem para você fazer alguma coisa. Pense como regras de segurança mais fácil seria se isso era tudo o que levou ..
jmoreno
Se o usuário não tiver permissão para executar o X, não dê a ele um botão que diz "Executar X". A melhor segurança é a ignorância - não informar ao usuário sobre as coisas que não podem fazer;)
Steven A. Lowe
1

Isso depende inteiramente do que está sendo feito.

Primeiro, qual é o comportamento real que você deseja? Se alguém está inserindo suas próprias informações, uma rejeição e uma caixa de diálogo dizendo, essencialmente, "Você não pode fazer isso" provavelmente estão corretas. Se esta é uma pessoa de entrada de dados, trabalhando a partir de uma pilha de formulários, provavelmente uma caixa de diálogo também é boa e a pessoa de entrada de dados pode colocar os formulários inválidos em uma pilha especial. Se você estiver processando em lote, não deseja interromper as coisas, mas sinalize-as e vá para a próxima.

Depois de ter o comportamento, você precisa decidir como implementá-lo. Ter um verificador de regras de negócios que lança uma exceção é provavelmente uma boa ideia. Retornar um código de retorno e transmiti-lo é outra coisa que pode dar errado, e você certamente não deseja que as entradas erradas fiquem mais longe.

Não se preocupe com as despesas de desempenho. No caso de um indivíduo digitar dados, é trivial comparado aos outros momentos envolvidos. Normalmente, o humano leva mais tempo nesse sistema. No caso de um trabalho em lotes, se as exceções são um problema de desempenho, você está inserindo muitos registros incorretos e, na realidade, manipular e reinserir todos eles será mais um problema do que as exceções.

David Thornley
fonte
0

Ter uma API de exceção robusta e bem projetada que seja consistente é muito apropriado. Usar isso para impor regras de negócios também pode ser apropriado. De fato, na minha experiência, quanto mais complicada a regra de negócios, maior a probabilidade de ser tratada dessa maneira. Geralmente, é tão fácil, se não mais fácil, escrever um sistema em que são esperadas exceções do que escrever uma lógica de ramificação autorizada.

Isso significa que regras simples que podem ser descritas em uma única frase geralmente devem ser implementadas de maneira preventiva ou autoritária, dependendo de qual seja. No entanto, se você tiver uma regra que seja multidimensional e exija mais de três ou quatro fatores (especialmente se a seleção desses fatores for baseada em um ou mais dos outros fatores), a codificação de exceção poderá ser mais sustentável. Freqüentemente, nesses casos, o caminho lógico terá muitas exceções precursoras que precisam ser lançadas (verifica o motivo pelo qual a ação não pode ser executada); então (ou vice-versa) há uma falha na segurança (para verificar se a ação está autorizada). ), às vezes haverá alguma lógica de acumulação autorizada que precisa ser verificada (disponibilidade de descendente / ancestral, precursor declara que os objetos devem ser colocados etc.)

Um benefício que deriva desse tipo de lançamento de exceção é que ele permite separar e reutilizar as exceções precursoras em várias áreas do seu projeto. (Essa é a essência da programação orientada a aspectos.) Ao fazer isso, você está encapsulando um aspecto específico das regras gerais de negócios em um componente independente e sustentável. Em geral, esses componentes corresponderão 1-1 às mensagens de erro lançadas. (Embora às vezes você tenha um componente que lança várias exceções diferentes, quase nunca deve ter a mesma exceção lançada de vários componentes.)

Na minha opinião, é mais difícil projetar sistemas baseados em exceções e o tempo de desenvolvimento inicial é mais longo, pois é necessário criar o processo de exceção em todos os N níveis. No entanto, esses sistemas geralmente acabam sendo muito mais estáveis. Embora nunca seja possível projetar um sistema que 'não falhe', o benefício do design baseado em exceções é que você está sempre antecipando falhas. Para a maioria das pessoas, o processo pode ser contra-intuitivo. É como pedir orientações e pedir que alguém lhe diga todas as ruas que você não deve ligar.

Justin Ohms
fonte