📜 ⬆️ ⬇️

M in MVC: why models are misunderstood and undervalued (translation)

(the article is very old, the questions raised in it are relevant to this day and are regularly raised in various discussions)

Many of you have probably noticed that I am writing a book about the Zend Framework. Recently, I finished drafts of two chapters: “Application Architecture on the Zend Framework” and “Understanding the Zend Framework”. The first chapter explains the architectural template Model-View-Controller (MVC) and the reasons why it has become the de facto standard for web applications. The second examines the relationship of MVC with the components of the Zend Framework, their structure and interaction.

Having completed both chapters, I realized that most of the time I described the model and its actual absence in the Zend Framework. In fact, no web framework offers us a full-fledged model (for reasons that I will explain later). And none of them gives a clear explanation to this circumstance. Instead, they consistently associate the concept of a model with a related, but not identical, concept of data access, which pretty much confuses everyone.
')
This side of frameworks has never attracted much attention. And yet it is precisely this that underlies a whole class of problems in those applications that are trying to use MVC in the same way as web application frameworks. Moreover, attempts to convey the idea of ​​the model to other developers are often reminiscent of hitting your head against the wall. I do not want to say that all developers are stupid or do not understand the idea itself, just none of them (regardless of whether they work with PHP or not) connect the model with the area that gives them meaning - the principles of object-oriented programming.

In this post, I will explore the models in the light of how developers relate them to the controllers and views in the applications and describe several strategies that can be used with the right models.

Models do not understand

Models can be described in different ways. In fact, only about this you can write a whole book, many did just that! As a rule, two model roles are described:

1. The model is responsible for maintaining state between HTTP requests.

As a matter of fact, any data - in the database, file, saved in a session or cached inside APC, should be saved between requests as an application state at the time of the last request. Remember, the model is not limited to the database. Even data obtained from web services can be represented as a model! Yes, even Atom feeds! Aspirants to quickly acquaint with the framework model never explain this, increasing misunderstanding.

Take, for example, the component I am developing, called Zend_Feed_Reader, which is actually a model. It reads news feeds, processes them, interprets data, adds constraints, rules, and by and large creates a convenient presentation of the underlying data. Without it, we have Zend_Feed (the best tool for reading news feeds at the moment), which requires a lot of work to get a complete model. In other words, Zend_Feed_Reader is a model, while Zend_Feed is limited to data access.

2. The model includes all the rules and restrictions, controls the behavior and use of this information.

For example, you write business logic for an order model in the supplying application and, according to the company's internal rules, a cash limit of 500 euros may be imposed on cash purchases. Purchases of more than 500 euros must be prohibited in your order model (they may require approval from a supervisor). The model must have the means to establish such restrictions.

Everything will become very clear as soon as you think about the meaning of the word “model”. In climatology, there are climate models that describe data, processes, expected behavior, and allow for the calculation of possible results. M in MVC is called a model for a reason. The model represents not only data, it represents the entire system in which this data is useful. Of course, the system can be so complex that it will need several interacting models, but you get the idea.

After reading these two points, you most likely began to realize something amazing. With the exception of the interface, any application can be represented as models. It is in them that the data are based, the rules of behavior based on them and, in some cases, even the output of this data. It is the model that is able to understand, interpret and present the data, providing them with meaningful use.

In programming, thick models are preferable to models with zero size.

Jamis Buck (Jamis Buck, the author of Capistrano, now working in 37signals) at one time described the concept of "Skinny controller, fat model." Chris Hartjes also wrote an article on this topic. I have always liked the simplicity of this concept, as it illustrates the key feature of MVC. Within the framework of this concept, it is considered that, as far as possible, the logic of the application (such as the business logic from the example above) is best placed in the model, and not the controller or view.

The presentation should be engaged only in the creation and display of an interface through which users can communicate the model about their intentions. Controllers are organizers that link data entered into an interface with model actions and send the output back, whichever view it represents. Controllers should define the behavior of the application only in terms of the relationship of user input to the model calls, but otherwise it should be clear that all the application logic is in the model. Controllers are modest creatures with a minimum of code that provide the conditions for orderly work.

By and large, php-developers do not quite understand what a model is. Many consider the model a beautiful word to denote access to the database, others equate it with different templates for accessing the database, such as Active Record, Data Mapper and Table Data Gateway. The frameworks very often promote this misconception, unintentionally, I'm sure, but energetically. Not fully understanding what a model is, why it’s such a great idea, and how it should be developed and deployed, developers inadvertently embark on a dark path leading to such development techniques that cannot be called anything other than poor.

A little mental exercise will give you reason to think. Imagine that you have just written the most wonderful web application in the world using the Zend Framework. The client is amazed, his enthusiasm (and money) is extremely pleasant. Unfortunately, they forgot to mention that their new CTO requires the use of symfony in all new applications and offers a very interesting amount for converting your application. Question: how easy will it be? Think about it for a second ...

If the logic of your application is tied to a model - you are on a horse! Symfony, like many (but not all) frameworks, accepts models regardless of what they are written on top of. You can transfer your model, its unit tests and auxiliary classes to symfony with little or no change. If you connected all this with controllers, you have problems. Do you really think Symfony can use the Zend Framework controllers? What function tests using the PHPUnit extension of the Zend Framework will magically work in some way? Both on. This is why controllers cannot replace models. They are almost impossible to reuse.

Misunderstood, undervalued, unloved: models in depression

Since developers very often underestimate the role of the model, limiting it to database access, as is done by default in 99.9% of frameworks, there is nothing surprising that they are not impressed by the theoretical ideals associated with it. Focusing on data access, developers completely let one very important point: classes of models are not connected with the current framework. They do not need a complicated installation, you simply create and use their objects.

Maybe we shouldn't blame the average developer for everything. Web applications have certain behavioral patterns that make this curve way more attractive - most of them are just very large data readers. If the data is almost not processed, only read, then the model will be very similar to the good old data access. Not the best of circumstances, so don't let the simplicity of reading the data fool you. Not all applications are limited to reading - some, no doubt, have to do with the data something beyond the limits of displaying them in an unchanged, appropriate form.

Models in PHP are losers. Since the advent of Smarty and his relatives all are passionate about views. Controllers are also very important, as they read data from the database and transfer them to templates (common VC interpretation). Yes, controllers are a logical evolution of a page controller injected into the brain, which is used by every PHP developer and his dog since PHP3. At least to most, this seems obvious. We will break the myth of the “controller = page controller” later.

And the models? Since they have no ideological appeal or similarity with old habits, people see them as banal “data access”. Like reference types in PHP, pointing to the same value in memory. The language has changed, but old ideas are still hiding behind the scenes, confusing our neural networks.

But wait ... because the developers still write working applications! And if they do not use models that contain application logic, then what the hell are they using ?!

Thick, stupid, ugly controllers: Humble yourself (Fat Stupid Ugly Controllers: SUC It Up)

Since the developers knew almost nothing about the models, they invented a new concept: thick, stupid, ugly controllers (TTUC). I came up with such a vivid definition for a reason, it seems very funny at 10 in the evening, after a few beers. And still polite than what I really think of them. (Fat Stupid Ugly Controllers - FSUC - FUC). They were invented because the models were unusual, alien and terrorist-like entities that no one dared to entrust at least something beyond the limits of access to data.

A typical TTUC reads data from the database (using the data abstraction layer, which developers call a model), processes it, checks, writes and transmits it to the view for display on the screen. He is incredibly popular. I would say that most framework users create them just as naturally as they created Page Controllers before. They are popular because the developers have realized that they can handle the controllers in much the same way as the page controllers - this is practically no different from the ancient method of using separate php files for each “page” of the application.

Did you notice anything unusual? TTUC performs all possible actions on the data. Why? Because in the absence of a model, the entire logic of the application moves to the controller, which makes it a peculiar mutant model! I did not just use the word "mutant". TTUCs are very big, bulky, ugly and definitely thick. There is a pseudo-programmer term that very accurately describes what is happening - “ bloated ”. They perform tasks for which they were never intended. This is the complete opposite of all the principles of object-oriented programming. And they are meaningless! For some mysterious reasons, developers prefer to use TTUCs instead of models, despite the fact that such controllers are actually just mutant models.

Remember our mental exercise? If you put everything in controllers, moving an application to another framework will be extremely difficult. Fans of Kent Beck call such a hard linking a “code with a smell” (code smell). And this is not the only source of smell in TTUC. TTUCS are large, with massive methods, multiple roles (usually one for each method), multiple repetitions of code, not rendered into external classes by functionality ... a tester's nightmare. Since there are a lot of frameworks, you cannot even correctly apply development through testing (TDD) without writing your own add-ons and the need to process request objects, sessions, cookies, and reset the input controller (Front Controller resets). And even in this case, you will have to test the presentation created by the controller, since it has no output methods that are independent of the views!

We continue to ask questions! How do you test the controller? How do you refactor the controller? How do you limit controller roles? What is the controller performance? Is it possible to create an instance of the controller outside the application? Can I consistently combine several controllers into one process and not go crazy? Why don't I use simpler classes and don't call them models? Did I even think about these questions?

Models are inevitable (like death and taxes)

A classic mistake is made in TTUC. Considering that the model idea is stupid and simple data access works best, developers unknowingly tie all the application logic to the controller. My congratulations! You have just created a model, a crappy, ugly mutant model, insisting that she is a controller. But this self-styled controller is extremely difficult to transfer, test, maintain and refactor (considering the refactoring concept from the real world ... it happens!) The nature of controllers is such that they are closely related to the underlying framework. You can execute the controller only after initialization of the entire MVC set of this framework (which most likely means dependence on dozens of other classes!)

You can go the other way - take out the application logic somewhere. Moving it from the controller to the model you get a lot of classes that do not depend on the framework used. Now you can spend days testing these breeding grounds for glitches using PHPUnit, never seeing the controller or view and torturing yourself with stupid restarts of the framework after each test. Considering them to be real classes with clearly defined roles, you can look at them from the right point of view, perform the appropriate refactoring and write truly supported code without duplicating it in many classes.

Models are inevitable. Someone may call TTKU a controller, but in fact we are a controller + model, an extremely inefficient replacement of the model. Some people will just laugh at all these fools who argue about the need for good independent domain models and continue to write confusing code. Let them laugh. After all, they will have to maintain and test their mess.

This is where most web application frameworks fail their users. They are surrounded by a huge amount of marketing nonsense, implicitly suggesting that they offer a complete model. Have you ever seen a framework that directly speaks something else? In the end, this is the MVC framework. Recognizing that a developer must write M on his own can make a bad impression. So they hide the truth in a variety of details scattered around the data access documentation, or do not mention it at all.

In fact, they offer only data access classes - this model reflects the characteristics of a particular application and should be developed independently after communicating with clients (you can choose a beautiful name for this - I personally prefer “extreme programming”). It must be tested, verified, updated, and the probability of success / failure will be unchanged, regardless of the framework used. A bad Rails application will remain a bad Code Igniter application.

Controllers do not have to protect data.

Another consequence of the general mistrust of the model is that the developers try to use it to the minimum and trust the controllers with the new role of data keepers (one of the main reasons for their mutation in TTUC). Do you want me even more to kindle a fire of general disagreement with me?

Some time ago, I wrote a project (Zend_View Enhanced), which will sooner or later be accepted into Zend Framework to introduce an object-oriented approach to creating complex views, and began to complain that controllers are the only method of transferring data from models to views. I thought that views can do without an intermediary and use View Helpers instead of it to read data directly from models. This will lead to an architecture in which for many pages that are accessible only to read requests, your controller action (Controller Action) will be ... empty. There will be no code in it. Amen!

The best controller for me is the lack of a controller.

I immediately encountered massive resistance.Very few people understood that an empty controller, where all interaction with the model was rendered into simple and reusable view helpers, would reduce duplicate code in controllers (a very large amount of duplicate code!) And eliminate the need to build chains of controller actions. Instead, I heard quite a few intricate expressions. Many thought that MVC works as follows: requests go to the controller, the controller receives data from the model, the controller sends data from the model to the view, the controller draws the view. In other words: controller, controller, controller, controller. Once I noticed that the community is just obsessed with controllers. Until now, it is very difficult to ensure that someone gives ideas to objects with data and allows them to read data from models on their own ...

Note: not everything is so bad - some people understood what it was about.

No objector noticed that this is actually a very old idea. In Java, the term view helper was introduced many years ago as a design pattern in J2EE, showing that view assistants can help views access models (only read, since all write operations must go through the controller!) Without an intermediary controller. Everything in MVC says that views should be aware not only of the arrays that the controller puts in their mouths, but also of the models that they display.

So why not go further! How many misses one model? Many of my ideas use several models, references to which are very often repeated. The view helper is one class, but to add repeated calls to controllers, we need to repeat these calls in a variety of methods!

To get rid of assistant views, one crazy decision was invented, in which controller actions were re-invented as reusable commands. If a view requires multiple models, you simply invoke several controllers in sequence. I often call this idea controller clutch (Controller Chaining) - its meaning is to create a service code that makes it possible to reuse controllers. You can translate it as: reuse of any class needed to perform a specific controller action. Do not forget - no controller can be used without initializing the entire framework. Although there are (always there!) Exceptions.

Models - classes, controllers - processes

My non-standard ideas must have tired you, but the above controller clutch requires more detailed consideration. Linking is often used to access several models or to combine the results of several representations or both for the other. The latter happens most often - if you don’t use view helpers to simplify the process, controllers almost always turn to models and pass data to views.

Imagine that you created three controllers, each of which creates a view. To create a new web page, you need to combine the three views into a single page by template (or layout). This is done by sequentially invoking all three controllers via Zend_Layout (or some other solution for assembling views into one layout / section). And now let's see what happened - three controllers mean that we execute the MVC stack three times. Depending on the application, this can lead to significant resource overhead. Just as an example of the magnitude of these costs, Symfony uses “components” as specialized types of controllers designed solely to mitigate a performance hit, but there is nothing like it in the Zend Framework.A similar idea in Rails caused a lot of complaints about performance drops. Common wisdom says that using multiple full controllers in frameworks is terribly inefficient and is the last hope in cases where there are no other reuse strategies.

I repeat, the controller clutch is a code with a smelt. It is inefficient, awkward and usually not needed.

The alternative, of course, is the use of partial representations (pieces of the template that can unite into one parent representation), which can directly interact with the model through the presentation assistant. Get rid of the controllers in general - in the end, there is no application logic in them, except for transferring user input to the corresponding model call (with the exception of TTUC).

The basic idea is that the model is just a collection of loosely coupled classes. You can create and use their copies anywhere - in other models, controllers and even views! On the other hand, the controller is an integral part of the overall process. You cannot reuse the controller without starting the entire process of creating request objects, dispatching, applying action assistants, initializing the view, and processing the returned response objects. It is costly and awkward.

At the end of the conversation

As you can see, this article was vital. I firmly believe in the need to implement the elegant principles of object-oriented programming in the MVC framework. That is why I did my best to promote Zend_View Enhanced in the Zend Framework and saw how it was approved, discussed and extremely successfully used. This was largely due to Matthew Weier O'Phinney and Ralph Schindler, who joined the promotion of this idea. Having forgotten about simple techniques and principles of OOP, we will get stuck in the fight against MVC, forgetting about its meaning. MVC is a great architectural pattern, but in the end, any of our interpretations of MVC and habitual beliefs are derived from OOP principles. Having forgotten about this, we will start doing Bad Things (TM).

I hope this stream of thoughts about the model in the model-controller-representation will be something enlightening and make you think. Your goal should be your own thinking - doubt everything and rise when something seems wrong to you. Relying on blind faith, we deserve everything that happens to us.

The M in MVC: Why Models are Misunderstood and Unappreciated

Source: https://habr.com/ru/post/175465/


All Articles