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. Utilizamos sqlalchemy
e grande parte da base de código existente em nossos modelos de banco de dados é pouco mais que um conjunto de propriedades mapeadas com um nome de classe, uma tradução quase mecânica de tabelas de banco de dados em objetos python.
No argumento, minha posição era de que o principal valor do uso de um ORM era o fato de você poder anexar comportamentos e algoritmos de baixo nível às classes mapeadas. Os modelos são primeiro as classes e secundariamente persistentes (eles podem ser persistentes usando xml em um sistema de arquivos, você não precisa se preocupar). Sua opiniã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 do banco de dados.
Certamente acho que existe uma distinção entre o que é lógica de negócios e deve ser separado, pois ele tem algum isolamento do nível mais baixo de como isso é implementado e lógica de domínio, que acredito ser a abstração fornecida pelas classes de modelo discuti no parágrafo anterior, mas estou tendo dificuldade em entender o que é isso. Tenho uma noção melhor do que pode ser a API (que, no nosso caso, é HTTP "ReSTful"), pois os usuários invocam a API com o que desejam fazer , diferente do que têm permissão para fazer e como ela é feito.
tl; dr: Que tipos de coisas podem ou devem seguir 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?
fonte
Respostas:
Eu estou principalmente com você; seu colega parece estar argumentando pelo antipadrão do modelo de domínio anêmico ou pela duplicação do modelo em um "modelo de persistência" sem nenhum benefício óbvio (estou trabalhando em um projeto Java onde isso foi feito, e é uma dor de cabeça maciça de manutenção, como significa três vezes o trabalho sempre que algo muda no modelo).
Regra geral: a classe deve conter lógica que descreva fatos básicos sobre os dados que são verdadeiros em todas as circunstâncias. A lógica específica de um caso de uso deve estar em outro lugar. Um exemplo é a validação, há um artigo interessante de Martin Fowler, onde ele afirma que deve ser considerado dependente do contexto.
fonte
Essa é uma decisão que realmente depende do tamanho e da escala previstos do que você está desenvolvendo. A abordagem mais rígida é limitar os tipos ORM a um componente de acesso a dados e usar POCOs em uma biblioteca comum como 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. Essa camada adicional é onde mora o seu "código de casos de uso". O código individual fracamente acoplado é chamado pela camada Facade / BI (por exemplo, Facade possui uma função ProcessOrder () que chama a lógica de negócios 1: M vezes para executar todas as etapas necessárias para realmente processar o pedido).
No entanto, tudo isso foi dito: muitas vezes essa quantidade de arquitetura é simplesmente um exagero desnecessário. Por exemplo, codifique especificamente para um site simples em que você não tem a 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á analisar o agrupamento ou um processo frequentemente perdido chamado "refatoração".
fonte
Lembre ao seu colega que você não precisa arquitetar demais os modelos como se fosse um projeto Java. Quero dizer, comparar dois objetos persistentes é comportamento, mas não é especificado pela camada de persistência. Portanto, a questão das 6 cervejas é: por que classes completamente independentes descrevem algo sobre a mesma coisa? Certamente, 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 maneira distinta de todo o resto. Se você dirige seu carro, lava ou rebenta, você manipula 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 que lidam com objetos persistentes - coloque-os em uma classe; você tem vários métodos de instância que lidam com validação - coloque-os em outro. Finalmente, misture os dois e pronto! Você conseguiu uma representação de modelo inteligente, autoconsciente e totalmente contida.
fonte
Além de outras respostas, preste atenção aos cavehats ocultos ao usar modelos de domínio avançados com um ORM.
Tive problemas ao injetar serviços polimórficos nas classes de modelo persistentes ao tentar obter algo como o seguinte pseudocódigo:
Nesse caso, uma organização pode exigir uma
HRService
dependência de construtor (por exemplo). Você geralmente não pode controlar facilmente a instanciação de suas classes de modelo ao usar um ORM.Eu estava usando o Doctrine ORM e o contêiner de serviço do Symfony. Eu tive que monitorar o ORM de maneira não tão elegante e não tive escolha a não ser separar persistência e modelos de negócios. Ainda não tentei com sqlachemy, pensei. O Python pode ser mais flexível que o PHP para essas coisas.
fonte