Modelos grossos vs. Lógica de negócios, onde você desenha a distinção?

15

Hoje entrei em um debate acalorado com outro desenvolvedor da minha organização sobre onde e como adicionar métodos às classes mapeadas do banco de dados. Usamos sqlalchemy , e a maior parte da base de código existente em nossos modelos de banco de dados é pouco mais que uma bolsa de propriedades mapeadas com um nome de classe, uma tradução quase mecânica de tabelas de banco de dados para objetos python.

No argumento, minha posição era de que o valor primário de usar um ORM era que você pode anexar comportamentos e algoritmos de baixo nível às classes mapeadas. Modelos são classes primeiro e secundariamente persistentes (eles podem ser persistentes usando xml em um sistema de arquivos, você não precisa se importar). Sua visão era de que qualquer comportamento é "lógica de negócios" e necessariamente pertence a qualquer lugar, exceto no modelo persistente, que deve ser usado apenas para persistência de banco de dados.

Eu certamente acho que há uma distinção entre o que é a lógica de negócios, e deve ser separado, já que tem algum isolamento do nível mais baixo de como isso é implementado, e lógica de domínio, que Acredito que seja a abstração fornecida pelas classes de modelo discutidas no parágrafo anterior, mas estou tendo dificuldade em colocar o dedo naquilo que é. Eu tenho uma noção melhor do que pode ser a API (que, no nosso caso, é HTTP "ReSTful"), em que os usuários invocam a API com o que eles querem fazer , distintos do que são permitido fazer, e como é feito.

tl: dr: Que tipos de coisas podem ou devem ir em um método em uma classe mapeada ao usar um ORM, e o que deve ser deixado de fora, para viver em outra camada de abstração?

    
por SingleNegationElimination 29.03.2012 / 06:05
fonte

4 respostas

10

Estou principalmente com você; seu colega parece estar discutindo sobre o antipadrão de modelo de domínio anêmico ou para duplicar o modelo em um "modelo de persistência" sem benefício óbvio (estou trabalhando em um projeto Java onde isso foi feito, e é uma dor de cabeça de manutenção massiva, pois significa três vezes o trabalho sempre que algo muda no modelo).

What kinds of things can or should go in a method in a mapped class when using an ORM, and what should be left out, to live in another layer of abstraction?

Regra geral: a classe deve conter uma lógica que descreva os fatos básicos sobre os dados que são verdadeiros em todas as circunstâncias. A lógica específica para um caso de uso deve estar em outro lugar. Um exemplo é a validação, há um interessante artigo de Martin Fowler onde ele aponta que deve ser considerado dependente do contexto.

    
por 29.03.2012 / 09:38
fonte
3

Este é um julgamento que realmente depende do seu tamanho e escala previstos do que você está desenvolvendo. A abordagem mais rígida é limitar os tipos de ORM a um componente de acesso a dados e usar POCOs em uma biblioteca comum como os tipos referenciados e usados por todas as camadas. Isso permitiria a separação física futura, bem como a separação lógica. Você também pode decidir que uma camada adicional deve existir entre a interface do usuário e a camada de lógica de negócios. Isso geralmente é chamado de camada Fachada ou Interface de Negócios. Esta camada adicional é onde o seu "código de caso de uso" vive. O código individual de baixo acoplamento é chamado pela camada Fachada / BI (por exemplo, o Facade possui uma função ProcessOrder () que solicita à lógica de negócios 1: M vezes para executar todas as etapas necessárias para processar a ordem).

No entanto, tudo isso foi dito: muitas vezes essa quantidade de arquitetura é simplesmente desnecessária. Por exemplo, codifique especificamente para um site da Web simples em que você não tem intenção de empacotar seus componentes para reutilização. É perfeitamente válido criar um site MVC e usar objetos EF para esse tipo de solução. Se o site precisar ser expandido posteriormente, você poderá investigar o clustering ou um processo que muitas vezes é perdido, chamado de "refatoração".

    
por 29.03.2012 / 06:54
fonte
3

Apenas lembre ao seu colega que você não precisa overarchitect os modelos como se este fosse um projeto Java. Quero dizer, comparar dois objetos persistentes é comportamento, mas nenhum é especificado pela camada de persistência. Então a questão da 6 cerveja é: por que classes completamente não relacionadas descrevem algo sobre a mesma coisa? Claro, a persistência é um aspecto grande o suficiente de um modelo para ser tratado separadamente, mas não o suficiente para garantir que ele seja tratado de forma distinta de todo o resto. Se você dirigir seu carro, lave-o ou puxe-o, manipulando seu carro o tempo todo.

Então, por que não apenas compor todos esses aspectos diferentes em uma única classe de modelo? Você precisa de vários métodos de classe lidando com objetos persistentes - coloque-os em uma classe; você tem vários métodos de instância lidando com validação - coloque-os em outra. Finalmente, misture os dois e voilà! Você tem uma representação de modelo inteligente, autoconsciente e totalmente contida aqui.

    
por 29.03.2012 / 09:00
fonte
1

Além de outras respostas, preste atenção aos ocultos ocultos ao usar modelos de domínio avançados com um ORM.

Eu tive problemas ao injetar serviços polimórficos nas classes de modelo persistentes ao tentar alcançar algo como o seguinte pseudocódigo:

Person john = new Person('John Doe')
Organisation company = organisation_repository.find('some id')
Employee our_collegue_john = company.hire(john)

Nesse caso, uma organização pode exigir um HRService como uma dependência de construtor (por exemplo). Você geralmente não pode controlar facilmente o instanciação de suas classes de modelo ao usar um ORM.

Eu estava usando o Doctrine ORM e o contêiner de serviços do Symfony. Eu tive que manipular o ORM de uma maneira não tão elegante e não tive escolha senão separar modelos de persistência e negócios. Eu não tentei ainda com sqlachemy, pensei. O Python pode ser mais flexível que o PHP para essas coisas.

    
por 18.01.2017 / 04:44
fonte