Modèles épais Vs. Business Logic, où faites-vous la distinction?

16

Aujourd'hui, je suis entré dans un débat animé avec un autre développeur de mon organisation sur où et comment ajouter des méthodes aux classes mappées de base de données. Nous utilisons sqlalchemy, et une grande partie de la base de code existante dans nos modèles de base de données n'est guère plus qu'un sac de propriétés mappées avec un nom de classe, une traduction presque mécanique des tables de base de données en objets python.

Dans l'argument, ma position était que la valeur principale de l'utilisation d'un ORM était que vous pouvez attacher des comportements et des algorithmes de bas niveau aux classes mappées. Les modèles sont d'abord des classes et secondairement persistants (ils peuvent être persistants en utilisant xml dans un système de fichiers, vous n'avez pas besoin de vous en soucier). Son point de vue était que tout comportement est une "logique métier" et appartient nécessairement n'importe où sauf dans le modèle persistant, qui ne doit être utilisé que pour la persistance de la base de données.

Je ne pense qu'il ya une distinction entre ce qui est la logique métier, et doit être séparé, car il a un certain isolement par rapport au niveau inférieur de la façon qui soit mis en oeuvre, et la logique de domaine, que je crois est l'abstraction fournie par les classes de modèle discuté dans le paragraphe précédent, mais j'ai du mal à mettre le doigt sur ce que c'est. J'ai une meilleure idée de ce que pourrait être l'API (qui, dans notre cas, est HTTP "ReSTful"), en ce sens que les utilisateurs invoquent l'API avec ce qu'ils veulent faire , distinct de ce qu'ils sont autorisés à faire et comment cela se fait.


tl; dr: Quels types de choses peuvent ou devraient aller dans une méthode dans une classe mappée lors de l'utilisation d'un ORM, et ce qui devrait être laissé de côté, pour vivre dans une autre couche d'abstraction?

SingleNegationElimination
la source
Il me semble que ce sont deux problèmes discutés en même temps, le problème de la persistance Les modèles sont d'abord des classes et secondairement persistants (ils pourraient être persistants en utilisant xml dans un système de fichiers, vous n'avez pas besoin de vous en soucier). et la raison du code. Habituellement, les choses s'éclaircissent, du moins pour moi, quand je me demande pourquoi le code est écrit, quelle exigence force ce code. Est-ce l'exigence des clients sur la façon dont le programme fonctionnera ou est-ce de la manière dont nous choisissons de le mettre en œuvre? Pour moi, le premier est la logique métier et le second ce que vous appelez la logique de domaine. Comment cela aide.

Réponses:

10

Je suis surtout avec toi; votre collègue semble plaider soit pour l' anti-modèle du modèle de domaine anémique, soit pour dupliquer le modèle dans un "modèle de persistance" sans aucun avantage évident (je travaille sur un projet Java où cela a été fait, et c'est un énorme casse-tête de maintenabilité, comme cela signifie trois fois le travail chaque fois que quelque chose change dans le modèle).

Quels types de choses peuvent ou devraient aller dans une méthode dans une classe mappée lors de l'utilisation d'un ORM, et qu'est-ce qui devrait être laissé de côté, pour vivre dans une autre couche d'abstraction?

Règle générale: la classe doit contenir une logique décrivant des faits de base sur les données qui sont vrais en toutes circonstances. La logique spécifique à un cas d'utilisation devrait être ailleurs. Un exemple est la validation, il y a un article intéressant de Martin Fowler où il fait valoir qu'il doit être considéré comme dépendant du contexte.

Michael Borgwardt
la source
+1, pour la deuxième partie de votre réponse. En ce qui concerne la première partie, je suis sûr de la raison pour laquelle vous dites que le modèle de domaine anémique nécessite autant de travail «supplémentaire» en cas de changement. Ma compréhension est que le changement se produira de toute façon, mais dans plusieurs classes différentes (ce qui est plutôt mauvais), mais c'est presque la même quantité de changement; Je suppose?
NoChance
1
@Emmad Kareem: Le commentaire portait sur la persistance spearate et les modèles de domaine. L'ajout d'une propriété au modèle nécessite ensuite de l'ajouter à deux classes de modèle (plutôt qu'à une) ainsi qu'à toutes les cartes entre elles (qui pourraient en théorie être automatiques, mais généralement l'asshat qui pensait que c'était une bonne idée d'avoir une "modèle de persistance" décide de justifier cette séparation en les rendant différents, par exemple, avoir des types de données qui correspondent plus étroitement au modèle de type DB), c'est-à-dire 2 + X fois la quantité de changement, avec X variant entre 0 et des heures de productivité perdue en raison de l'obscurité problèmes de cartographie.
Michael Borgwardt
3

Il s'agit d'un jugement qui dépend vraiment de la taille et de l'échelle prévues de ce que vous développez. L'approche la plus rigide consiste à limiter les types ORM à un composant d'accès aux données et à utiliser des POCO dans une bibliothèque commune comme types référencés et utilisés par toutes les couches. Cela permettrait une séparation physique future ainsi qu'une séparation logique. Vous pouvez également décider qu'une couche supplémentaire doit exister entre l'interface utilisateur et la couche de logique métier. Ceci est généralement appelé couche de façade ou d'interface métier. Cette couche supplémentaire est l'endroit où réside votre "code de cas d'utilisation". Le code individuel faiblement couplé est appelé par la couche Facade / BI (par exemple, Facade a une fonction ProcessOrder () qui appelle la logique métier 1: M fois pour effectuer toutes les étapes nécessaires au traitement de la commande).

Cependant, tout cela étant dit: souvent, cette quantité d'architecture est simplement une surpuissance inutile. Par exemple, codez spécifiquement pour un site Web simple où vous n'avez pas l'intention d'empaqueter ses composants pour les réutiliser. Il est parfaitement valable de créer un site Web MVC et d'utiliser des objets EF pour ce type de solution. Si le site doit évoluer plus tard, vous pouvez envisager un clustering ou un processus souvent perdu, appelé «refactoring».

Sean Chase
la source
3

Rappelez simplement à votre collègue que vous n'avez pas besoin de surarchitecturer les modèles comme s'il s'agissait d'un projet Java. Je veux dire, comparer deux objets persistants est un comportement, mais ce n'est pas spécifié par la couche de persistance. Donc, la question des 6 bières est la suivante: pourquoi avoir des classes complètement indépendantes décrivant quelque chose à propos de la même chose? Bien sûr, la persistance est un aspect assez important d'un modèle pour être traité séparément, mais pas assez pour justifier qu'il soit traité indépendamment de tout le reste. Si vous conduisez votre voiture, lavez-la ou la cassez, vous manipulez votre voiture tout le temps.

Alors pourquoi ne pas simplement composer tous ces différents aspects en une seule classe de modèle? Vous avez besoin d'un tas de méthodes de classe traitant des objets persistants - mettez-les dans une classe; vous avez un tas de méthodes d'instance traitant de la validation - mettez-les dans une autre. Enfin, mélangez les deux et le tour est joué! Vous avez obtenu une représentation de modèle intelligente, consciente de vous-même et entièrement contenue ici.

Filip Dupanović
la source
1

En plus des autres réponses, faites attention aux cavehats cachés lorsque vous utilisez des modèles de domaine riches avec un ORM.

J'ai eu des problèmes pour injecter des services polymorphes dans les classes de modèles persistants lorsque j'essayais de réaliser quelque chose comme le pseudocode suivant:

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

Dans ce cas, une organisation peut exiger un HRService dépendance en tant que constructeur (par exemple). Vous ne pouvez généralement pas contrôler facilement l'instanciation de vos classes de modèle lorsque vous utilisez un ORM.

J'utilisais Doctrine ORM et le conteneur de service de Symfony. J'ai dû monkeypatch l'ORM d'une manière pas si élégante et n'avais pas d'autre choix que de séparer la persistance et les modèles commerciaux. Je n'ai pas encore essayé avec sqlachemy, pensai-je. Python pourrait s'avérer plus flexible que PHP pour ce genre de choses.

abstrus
la source