web-dev-qa-db-fra.com

Oui ou non: les modèles dans MVC doivent-ils contenir une logique d'application?

Hier, j'ai eu une discussion avec l'un de nos développeurs concernant MVC, plus précisément sur le rôle du composant de modèle dans MVC.

À mon avis, un modèle devrait simplement contenir des propriétés et presque aucune fonctionnalité, il y a donc aussi peu de méthodes que possible dans les classes de modèle.

Mon collègue pense cependant que les modèles pourraient et devraient avoir plus que cela et offrir beaucoup plus de fonctionnalités.

Voici un exemple dont nous avons discuté.

exemple 1

Disons que nous voulions créer un blog. Un blog contient des articles et des balises. Chaque article peut avoir plusieurs balises et chaque balise peut appartenir à plusieurs articles. Nous avons donc ici une relation m: n.

En pseudocode, cela ressemblerait probablement à ceci:

class Article{
    public int id;
    public String title;
    public String content;
    public Tag[] tags;

    // Constructor
    public void Article(id, title, content, tags){
        this.id = id;
        this.title = title;
        this.content = content;
        this.tags = tags;
    }
}

class Tag{
    public int id;
    public String name;

    // Constructor
    public Tag(id, name){
        this.id = id;
        this.name = name;
    }
}

Supposons maintenant que nous travaillons en vrac ici, ce qui signifie qu'il peut arriver que nous ayons une instance de l'article qui n'a pas encore de balises, nous allons donc utiliser un appel Ajax (à notre backend qui a une base de données contenant toutes les informations) pour obtenir les tags qui appartiennent à notre article.

Voici la partie délicate. Je crois que l'obtention des données backend via Ajax + JSON devrait être le travail du contrôleur en utilisant une classe dédiée qui traite la demande ajax en utilisant un analyseur:

class MyController{
    private void whatever(articleID){
        Article article = (Article) ContentParser.get(articleID, ContentType.ARTICLE);
        doSomethingWith(article);
    }
}

public abstract class ContentParser{
    public static Object get(int id, ContentType type){
        String json = AjaxUtil.getContent(id, type.toString()); // Asks the backend to get the article via JSON
        Article article = json2Article(json);

        // Just in case
        Tag[] tags = article.tags;
        if (tags == null || tags.length <= 0){
            json = AjaxUtil.getContent(article.id, ContentType.TAGS); // Gets all tags for this article from backend via ajax
            tags = json2Tags(json);
            article.tags = tags;
        }

        return article;
    }

    // Does funky magic and parses the JSON string. Then creates a new instance of Article
    public static Article json2Article(String json){
        /*
         ...
        */
        return new Article(id, title, content, tags);
    }

    // Does funky magic and parses the JSON string. Then creates a new instance of Tag
    public static Tag[] json2Tags(String json){
        /*
         ...
        */
        return tags;
    }

}

Exemple 2

Mon collègue estime que cela rompt avec l'idée de MVC, il suggère que le modèle prenne soin de cela:

class Blog{
    public int id;
    public String title;
    public Article[] articles;

    // Constructor
    public Blog(id, title, articles){
        this.id = id;
        this.title = title;
        this.articles = articles;
    }

    public void getArticles(){
        if (articles == null || articles.length <= 0){
            String json = AjaxUtil.getContent(id, ContentType.ARTICLE); // Gets all articles for this blog from backend via ajax
            articles = json2Articles(json);
        }
        return articles;
    }

    private Article[] json2Articles(String json){
        /*
         ...
        */
        return articles;
    }

}

class Article{
    public int id;
    public String title;
    public String content;
    public Tag[] tags;

    // Constructor
    public Article(id, title, content, tags){
        this.title = title;
        this.content = content;
        this.tags = tags;
    }

    public Tag[] getTags(){
        if (tags == null || tags.length <= 0){
            String json = AjaxUtil.getContent(id, ContentType.TAGS); // Gets all tags for this article from backend via ajax
            tags = json2Tags;
        }
        return tags;
    }

    // Does funky magic and parses the JSON string. Then creates a new instance of Tag
    private Tag[] json2Tags(String json){
        /*
         ...
        */
        return tags;
    }
}

Et en dehors du modèle, vous feriez: blog.getArticles(); ou article.getTags(); pour obtenir les balises sans se soucier de l'appel ajax.

Cependant, aussi pratique que cela puisse être, je pense que cette approche rompt avec MVC car à la fin de la journée, tous les modèles seront remplis de méthodes qui font diverses choses géniales et les classes de contrôleur et d'assistance ne font presque rien.

Dans ma compréhension de MVC, les modèles ne doivent contenir que des propriétés et un minimum de "méthodes d'assistance" à l'intérieur. Par exemple, un modèle "Article" pourrait proposer une méthode getNumOfTags () mais il ne devrait pas effectuer d'appels Ajax de lui-même.

Alors, quelle approche est correcte?

30
Timo

En général, j'essaie de garder les contrôleurs simples en termes de logique aussi. Si la logique métier est requise, elle ira jusqu'aux classes de "couche de service" pour la gérer. Cela évite également la répétition de tout code/logique, ce qui rend finalement l'ensemble du projet plus maintenable si la logique métier devait changer. Je garde simplement les modèles comme des objets d'entité.

Je pense que la réponse ci-dessus le résume bien, il est facile de trop concevoir un projet basé sur des modèles de conception: choisissez ce qui fonctionne pour vous et est le plus facile à entretenir/efficace.

26
Danny Brady

Vous devriez arrêter de traiter le "modèle" dans MVC comme une classe. Le modèle n'est pas une classe ou un objet. Le modèle est une couche (dans le MVC moderne, il y a eu quelques évolutions depuis le début du concept). Ce que les gens ont tendance à appeler des "modèles" sont en fait objet de domaine (Je blâme Rails pour cette masse-stupidité).

La logique d'application (interaction entre les structures logiques de domaine et l'abstraction de stockage) doit faire partie de la couche modèle. Pour être plus précis: il doit être à l'intérieur du Services.

L'interaction entre la couche de présentation (modèles, vues, mises en page, modèles) et la couche modèle ne doit se produire que via ces services.

L'application n'a pas sa place dans les contrôleurs. Les contrôleurs sont des structures de couche de présentation et ils sont responsables de la gestion des entrées utilisateur. Veuillez ne pas y exposer d'objets de domaine.

10
tereško

Correct? Soit. Ils compilent tous les deux, non?

Les astuces pratiques sont sympas, pourquoi ne pas les utiliser si vous le pouvez? Cela étant dit, comme vous l'avez souligné, vous pouvez obtenir des modèles gonflés si vous y mettez toutes sortes de logiques. De même, cependant, vous pouvez obtenir des contrôleurs gonflés quand ils font des quantités massives dans chaque action. Il existe des moyens d'extraire des éléments de l'un ou de l'autre, si cela est également nécessaire.

À la fin de la journée, tous les modèles de conception sont des lignes directrices. Vous ne devriez pas suivre aveuglément une règle, simplement parce que quelqu'un d'autre l'a dit. Faites ce qui fonctionne pour vous, ce que vous pensez donne du code propre et extensible et frappe toutes les métriques vous pensez à faire du bon code.

Cela étant dit, pour un vrai MVC idéaliste, je dirais que les modèles ne doivent pas avoir d'actions externes, ce sont des représentations de données, rien de plus. Mais n'hésitez pas à être en désaccord :-)

6
Alexander R

Votre suggestion sur les modules (sans logique métier à l'intérieur) ressemble plus à de parler d'objets de valeur. La suggestion de votre collège ressemble plus à des objets de domaine.

À mon avis, le concept qui sera utilisé dépend du cadre qui est utilisé (c'est la vue pratique, la plus philosophique est ci-dessous). Si le framework est utilisé, il définit généralement des règles sur la façon dont vous devez implémenter chaque composant. Par exemple, nous pouvons examiner différents cadres MVC. Dans le framework Cairngorm de Flex, nous avons les deux. Les VO (objets de valeur) sont principalement utilisés pour les liaisons à la vue, tandis que les DO (objets de domaine) contiennent la logique métier. Si nous regardons l'implémentation MVC d'ASP.NET, nous avons là un modèle qui contient au moins les données (VO) mais aussi une certaine validation (si nécessaire). Voyons un framework UI MV * - par exemple Backbone.js. La documentation de Backbone indique:

Les modèles sont au cœur de toute application JavaScript, contenant les données interactives ainsi qu'une grande partie de la logique qui les entoure: conversions, validations, propriétés calculées et contrôle d'accès.

Si nous examinons le MVC traditionnel fourni par Smalltalk , nous voyons que: "Modèle: gère le comportement et les données du domaine d'application", nous avons donc un certain comportement, pas seulement des données simples.

Pensons pratiquement, si nous n'avons pas de logique dans le modèle, nous devrions probablement mettre toute la logique applicative et métier dans le contrôleur.

Concentrons-nous maintenant sur un exemple concret. Imaginez que nous ayons un modèle qui est un graphique. Nous voulons y trouver le chemin le plus court entre deux nœuds. Une bonne question est de savoir où mettre l'algorithme qui trouve le chemin le plus court? C'est une sorte de logique métier, non? Si nous regardons les principaux avantages de MVC (réutilisation de code, DRY etc.), nous pouvons voir que si nous voulons réutiliser notre modèle de la meilleure façon possible, nous devons implémenter le chemin le plus court à l'intérieur. L'algorithme du chemin le plus court dépend généralement de la représentation interne du graphique (ou au moins pour les meilleures performances de l'algorithme) mais cette représentation est encapsulée dans le modèle, malheureusement nous ne pouvons pas réutiliser le chemin le plus court complet pour la représentation matricielle et la liste des voisins donc ce n'est pas une bonne idée de le mettre dans le contrôleur.

Donc, pour conclure, je peux dire que cela dépend de vos besoins (principalement). Le but MVC traditionnel est d'être utilisé dans l'interface utilisateur (à l'intérieur de GoF

La triade de classes Model/View/Controller (MVC) [décrite pour la première fois par Krasner et Pope en> 1988] est utilisée pour créer des interfaces utilisateur dans Smalltalk-80.

)

maintenant, nous l'utilisons dans différents domaines - uniquement l'interface utilisateur, pour les applications Web, etc. Il ne peut pas être utilisé sous sa forme pure à cause de cela. Mais de toute façon, à mon avis, la meilleure séparation des préoccupations peut être obtenue en isolant la logique métier dans le modèle et la logique d'application dans le contrôleur.

6
Minko Gechev

En bref, je pense que le modèle ne devrait être que des données qui seront envoyées à votre vue. Il aide à conduire le paradigme MVC dans d'autres aspects de votre application.

Si vous essayez de ne pas casser le modèle MVC, vos données doivent toutes être renvoyées en tant que modèle commercial à votre contrôleur et décompressées dans votre ViewModel. Demandez le côté serveur d'informations, puis envoyez tout. Si vous devez effectuer des demandes JSon, il doit s'agir d'un service de repos ou d'appels à un contrôleur. Avoir ces getTags et getArticles rend les choses très compliquées ... si votre avis décide maintenant sur lequel appeler ... Je ne comprends pas pourquoi vous n'avez pas cette information n'est pas disponible à l'avance. L'utilisation de méthodes statiques est la même approche sous un angle différent.

J'ai trouvé préférable que mes actions de contrôleur appellent un service injecté qui fait la magie et utilise les modèles dans l'application Web MVC pour renvoyer les informations. Cela rend les choses plus propres et accentue davantage la séparation des préoccupations. Vos actions de contrôleur deviennent alors très maigres et il est clair ce qu'elles font.

Je crois que commencer par traiter le modèle comme complètement stupide pourrait faire beaucoup pour trier certains de ces problèmes architecturaux que je vois dans ce code.

4
Jonathan