Utilisation d'objets métier dans les modèles de vue

11

Lors de l'utilisation d'objets métier réutilisables, quelle est la meilleure pratique lors de la création de modèles de vue?

Nous utilisons un objet que nous appelons Builderpour construire nos modèles de vue. Un générateur pour chaque unité logique de vues (commandes, utilisateurs, etc.), où chaque unité peut contenir un certain nombre de modèles de vue différents (les commandes contiennent un résumé, des lignes de commande, etc.).

Un générateur peut extraire des données via un ou plusieurs objets métier standard afin de créer un modèle de vue.

Qu'est-ce qui est considéré comme la meilleure pratique lorsqu'il s'agit d'utiliser des objets / modèles métier dans les modèles de vue?

Approche 1

Autoriser l'utilisation d'objets métier dans le modèle de vue?

//Business object in some library
public class Order
{
    public int OrderNum;
    public int NumOrderLines;
    //...
}

//Order builder in website
public class OrderBuilder
{
    public OrderSummary BuildSummaryForOrder(int OrderNum)
    {
        Some.Business.Logic.Order obOrder = Some.Business.Logic.GetOrder(OrderNum);
        //Any exception handling, additional logic, or whatever

        OrderSummary obModel = new OrderSummary();
        obModel.Order = obOrder;

        return obModel;
    }
}

//View model
public class OrderSummary
{
    public Some.Business.Logic.Order Order;
    //Other methods for additional logic based on the order
    //and other properties
}

Approche 2

Prendre uniquement les données nécessaires des objets métier

//Business object in some library
public class Order
{
    public int OrderNum;
    public int NumOrderLines;
    //...
}

//Order builder in website
public class OrderBuilder
{
    public OrderSummary BuildSummaryForOrder(int OrderNum)
    {
        Some.Business.Logic.Order obOrder = Some.Business.Logic.GetOrder(OrderNum);
        //Any exception handling, additional logic, or whatever

        OrderSummary obModel = new OrderSummary()
        {
            OrderNum = obOrder.OrderNum,
            NumOrderLnes = obOrder.NumOrderLines,
        }

        return obModel;
    }
}

//View model
public class OrderSummary
{
    public int OrderNum;
    public int NumOrderLines
    //Other methods for additional logic based on the order
    //and other properties
}

Je peux voir les avantages et les inconvénients des deux, mais je me demande s'il y a une approche acceptée? Dans l'approche 1, il n'y a pas de duplication de code autour des modèles, mais cela crée une dépendance à la logique métier. Dans l'approche 2, vous prenez uniquement les données nécessaires à la vue, mais vous dupliquez le code autour des modèles.

Andy Hunt
la source

Réponses:

12

L'option 1 crée un couplage étroit entre le modèle de domaine et la vue. Cela va à l'encontre des modèles de vue très difficiles à résoudre.

Une vue modélise la «raison de changer» si la vue elle-même change. En plaçant un objet de modèle de domaine dans le modèle de vue, vous introduisez une autre raison de changer (par exemple, le domaine a changé). Il s'agit d'une indication claire d'une violation du principe de responsabilité unique. Le fait d'avoir deux raisons ou plus de changer conduit à des modèles de vue qui nécessitent beaucoup de maintenance - probablement plus que le coût de maintenance perçu de la duplication entre les modèles de domaine / vue.

Je préconiserais toujours l'approche 2. Il est souvent vrai que les modèles de vue peuvent sembler très similaires, voire identiques aux objets de modèle de domaine, mais la distinction, comme je l'ai mentionné, est leurs différentes raisons de changement.

MattDavey
la source
Ai-je raison de penser que par «raison de changer», vous entendez le changement dans un sens de maintenance, et non pas dans un sens de mise à jour (par exemple, un événement ui)?
Andy Hunt
@AndyBursh yep c'est correct - voir cet article , en particulier la ligne "Robert C. Martin définit une responsabilité comme une raison de changer, et conclut qu'une classe ou un module devrait avoir une, et une seule, raison de changer."
MattDavey
J'aime votre réponse mais quelques réflexions ... Le modèle de vue ne change pas nécessairement juste parce que le modèle change. Ce n'est que si vous liez ou utilisez une propriété spécifique qui a changé que ce sera un problème puisque votre référence est à l'objet entier. Le fait d'avoir une référence à l'objet domaine facilite les modifications et l'enregistre à nouveau. Vos méthodes de sauvegarde dépendent également de l'objet de domaine, vous devez donc reconvertir le modèle de vue ou configurer votre méthode commerciale pour accepter les modèles de vue, ce qui n'est pas bon non plus. Je pense toujours que le numéro 2 a le plus de sens, mais seulement de deux cents.
KingOfHypocrites
Si vous ne pouvez pas avoir d'objets de domaine dans une machine virtuelle, comment représenteriez-vous quelque chose de plus compliqué comme un tableau d'ordres?
Jeff
Cela signifie donc que des éléments tels que, par exemple, le formatage d'un horodatage pour l'affichage utilisateur, devraient appartenir à la couche d'affichage, pas à la couche de domaine, et les objets de niveau de domaine doivent renvoyer uniquement un horodatage brut et non formaté aux objets d'affichage et ces derniers sont ce qui devrait contenir la logique du formateur?
The_Sympathizer
2

L'option 1 est préférable car elle évite la duplication de code. C'est ça.

Si le modèle de domaine change de manière significative, il est presque certain que la vue devra de toute façon changer. Avec l'option 2, vous devez alors changer le modèle de vue ET le générateur ainsi que la vue elle-même. Ce genre de chose est un poison absolu pour la maintenabilité. YAGNI.

Le point d'avoir un modèle de vue séparé est de garder un état qui n'a de sens que pour la vue (par exemple quel onglet est actuellement sélectionné) séparé du modèle commercial. Mais les données d'entreprise elles-mêmes devraient être réutilisées plutôt que dupliquées.

Michael Borgwardt
la source
YAGNI - l'assassin secret de la résolution de la plupart des problèmes de conception de logiciels.
Martin Blore
6
Je suis désolé mais c'est un conseil horrible pour toutes les applications, sauf les plus triviales. Les modèles de vue n'ont pas d'état. Ce sont des objets de transfert de données. L'onglet sélectionné fait partie de la STRUCTURE de la vue et n'a RIEN à voir avec les DONNÉES dans le modèle de vue. La maintenance n'est pas un cauchemar si vous structurez correctement votre programme et utilisez quelque chose comme Automapper pour hydrater vos modèles de vue.
Lucifer Sam
"Si le modèle de domaine change de manière significative, il est presque certain que la vue devra de toute façon changer." - D'accord. Mais qu'en est-il lorsque vous avez un petit changement dans le domaine? Avec la première option, chaque petite modification apportée au domaine (même en renommant simplement une propriété) nécessite une modification correspondante de la vue. C'est également un poison absolu pour la maintenabilité.
MattDavey
@MattDavey: si vous renommez une propriété, alors avec un modèle de vue distinct, vous devez également changer la vue (ou tout autre mappage entre le domaine et le modèle de vue) et avoir maintenant deux noms différents pour la même chose, ce qui est certain de créer de la confusion.
Michael Borgwardt
@Lucifer Sam: nous avons évidemment des concepts très différents de ce qu'est un modèle de vue. La vôtre me semble très, très bizarre, comme si vous décriviez des applications mainframe pour des terminaux stupides, mais certainement pas des applications Web modernes ou de gros clients.
Michael Borgwardt
2

Les principes et les mantras sont parfois précieux pour guider la conception ... mais voici ma réponse pratique:

Imaginez que vos modèles de vue soient sérialisés en JSON ou XML. Si vous essayez de sérialiser vos modèles de domaine, vous allez vous retrouver avec un désordre hideux de texte et très probablement rencontrer des problèmes avec les références circulaires et d'autres problèmes.

Le but d'un modèle de vue n'est pas de regrouper des modèles de domaine afin que la vue puisse les consommer. Au lieu de cela, le modèle de vue devrait être un modèle complètement plat de la vue ... la chose réelle que vous regardez à l'écran. Votre logique de vue ne doit concerner que la structuration des données présentes dans le modèle de vue.

Idéalement, votre modèle de vue doit être composé presque entièrement de chaînes pré-formatées. Pensez-y ... vous ne voulez même pas de date ou d'heure décimale dans votre modèle de vue, car vous êtes coincé à faire une logique de formatage en C #, Javascript, Objective-C, etc.

Lucifer Sam
la source
2
Je n'ai jamais eu de problème avec la sérialisation des modèles de domaine. Et tout convertir en chaînes dans un modèle? Sérieusement?
Michael Borgwardt
3
@MichaelBorgwardt Oui, c'est ce qu'un modèle de vue DEVRAIT être. Vous ne voulez pas sérialiser vos modèles de domaine et les envoyer partout. Toute logique métier doit rester en toute sécurité à la maison en un seul endroit. Les vues doivent cependant être flexibles et pouvoir être rendues sur n'importe quel appareil, c'est pourquoi vous souhaitez séparer complètement votre STRUCTURE, DONNÉES et STYLE.
Lucifer Sam
Désolé, mais c'est un conseil horrible pour toute application, point final. Cela conduit à des applications sur-conçues pleines de code en double qui sont exactement l'opposé de flexible.
Michael Borgwardt
1
@MichaelBorgwardt, il semble que vous ayez l'habitude de travailler avec des modèles de domaine anémiques où les entités ne sont guère plus que des sacs de propriétés avec peu ou pas de comportement. Dans ce cas, oui, un modèle DTO / View serait essentiellement un doublon. Cependant, si vous avez un modèle de domaine riche avec des relations complexes, une couche de DTO / modèles de vue devient nécessaire et ils ne seront pas si similaires aux entités de domaine.
MattDavey
@MattDavey: Il semble que les modèles de domaine avec lesquels vous avez l'habitude de travailler ne sont pas seulement des kleptocrates riches mais véritables. Je n'aime pas non plus les modèles anémiques, mais ce sont toujours des modèles, et leur comportement devrait se limiter à représenter le domaine. Principe de responsabilité unique et tout ça ...
Michael Borgwardt