Well, the headlines are sensational, but I’m not a clicker. Considering that talking about big and empty things (like the best language) can be controversial, I invite you to approach this article with a play-watching mentality, not a courtroom mentality.
In my opinion, backend frameworks, organizations like MVC, have become obsolete.
In the past, applications were created in MVC folders, and each folder was a module. The responsibilities of MVC are as follows:
-
The Controller binds to a route, processes the request parameters, creates objects that are visible throughout the request, and does some business logic. Models are constructed from the database, and it is possible to create/modify models and save them to the database. Finally, the Controller responds to the user’s request through the View or returns a redirected message. Basically the business logic is implemented in the Controller. (For convenience, “business logic” refers to the business logic of Controller.)
-
Each Model usually corresponds to one entity of the database. In addition to storing data, the Model class provides methods that are relevant to its identity. For example, the User class provides the Authenticate method to verify passwords. Model objects are also typically responsible for verifying that the parameters they construct are correct.
-
The View is responsible for rendering the template with the given parameters and returning it to the user as a response. Views are typically written in the template language provided by the framework.
Now, a back-end application that is still divided by MVC seems a little out of place.
History of back-end MVC
Let’s start with the history of back-end MVC. MVC originally originated from the development of client programs, aiming to decouple Model and View with Controller. If I remember correctly, Gang of Four’s Design Patterns mentions MVC as an example of a client application.
Similar to client-side development, back-end applications start with only a View layer. A long, long time ago, back-end programs worked like this: a user would come in with an HTTP request, and the server would call a program through CGI to generate text in response. Most of the back-end programs of this era look like template languages (ever see JSP/PHP written by a beginner?). . Because the main thing they do is take data from user input and the database and concatenate strings to generate text. As the back-end programs began to evolve more and more complex, a single layer of View was no longer suitable. Because of the need to separate the logic from the View, the back-end application starts to follow the same path as the client application, separating MVC. Thus, the part responsible for routing and business logic processing becomes Controller, and the part responsible for data processing and persistence becomes Model.
Although the back-end application also does MVC split, it is actually different from the client-side MVC. On the client side, the Controller decouples the View from the Model. Changes to the View are passed to the Model through the Controller, and the latest data of the Model is passed back to the View through the Controller. The View: Controller: Model ratio is usually N: 1: N, where each View basically corresponds to one Model.
However, in back-end applications, the View and Model usually do not have a strong correspondence. CRUD in general is basically the Controller (business logic) running around the Model (data layer). View often plays a walk-on role.
Do you still need a V layer
In client-side MVC, the View plays a tripartite role against the other two. The user’s input goes through the View, and the underlying data changes are fed back to the user through the View.
However, in the back-end MVC, View’s status is shaky and dispensable. As mentioned earlier, controllers are bound to routes and receive requests; Controller renders the template and sends the response. Unlike the client, the Controller only uses the View when rendering the template. View was cut in half. It’s a double whammy. The Controller doesn’t have to render the template to send the response, it might just redirect; Or, more commonly, the Controller directly jsonizes an object and responds to it to the user. So how much of View is left?
Some backend frameworks offer jSON-formatted templates, more or less trying to salvage views from obsolescence. Unfortunately, it didn’t help. In the past, a View was usually made up of three parts: HTML code, control flow statements, and context at render time. If you provide a JSON-formatted template, the first two parts of the View are not needed, just the context at render time. But why do I need to render a JSON template, just create an instance from the context data, and generate a JSON string from it? I have an extra class for responses, but I don’t have a JSON template, and I probably don’t have to leave a folder for the View.
The few back-end applications I’ve worked on recently had no room for a View. All responses are in JSON format and are jSON-formatted by a specific class. In many cases, a back-end application is either a component in a larger back-end system or needs to deal with clients from multiple sources. Usually they just act as data guardians, API enforcers, responding only to JSON data. What the recipient wants to do with the data is up to them. It may be used for rendering front-end pages, or stored in a client database, or for further analysis. The View has degenerated to not even a layer.
Is M responsible for data entities or data access
After the shaky View, then the awkward Model. Model is the incarnation of data, back-end development of thousands of changes, the core is data processing. The Model is, so to speak, sitting in the golden seat. However, in my opinion, the current common practice of dividing only one Model package is not clear enough.
In my humble opinion, the Model in the back end actually does two things. One is to represent the data entity, and the other is to be responsible for accessing the data. According to the principle of single responsibility, it is wrong for Model to wear two hats. The data entity is one thing, and the access to the corresponding data entity is another, and the two should not be mixed.
Suppose that saving an Account requires a transaction in which the Account and Balance entities are updated. Here’s how it works in Rails:
# always save Account in a transaction
Account.transaction do
balance.save!
account.save!
endCopy the code
The question is, where does this code go? One way to do that is to put it in Controller, but the way you store accounts, shouldn’t you put it in Account? The other option is to put it in the Account class, but why not put it in the Balance class, which also holds the Balance transaction. As a programmer, you can’t play favorites in this matter. If you provide daOs as an intermediate layer, the scruple of “favoritism” is eliminated. The difference between this save with transaction and the save method of the Account class is also hierarchical.
In addition, classes in the Model layer do not necessarily correspond to tables in the database. The assumption that each Model knows how to persist its own data is unsustainable. If you don’t distinguish between data entities and the components that access them, you will one day be in danger of living up to your name.
A good example is that SQLAlchemy provides the Session class to perform access operations (transactions, saves, etc.) on concrete data (Model), so that with a little wrapping, we can separate out a data access layer and avoid the entanglement between data entities and data access.
session = Session() try: account = session.query(Account).get(...) balance = session.query(Balance).get(...) . Session.com () except: session.rollback()Copy the code
C: Everything
Having made fun of View and Model, it’s time for the last Controller. While View is responsible for presentation and Model is responsible for data, the responsibilities of Controller are not clear. A Controller is a basket into which you can put anything. Anything you can’t distinguish between View and Model, put it in Controller. So, in MVC, controllers tend to be the most bloated.
Finally, one day, we decided to clean up the chaotic environment of Controller. A common practice is to break up the Controller’s functions on a route into smaller functions. These small functions do not bind routes and are purely abstractions of business logic. After the split, the Controller is no longer bloated and the abstracted business logic can be reused. Maybe the Controller could have been split into two parts, one for binding routing and the other for business logic.
-
The part of binding route is responsible for the integrity and correctness of request data, and operations such as traffic limiting and authentication. In its eyes, it sees HTTP packets.
-
The part of business logic responsible for specific business processing. In its eyes, it sees the user’s actions.
In this way, the implementation of the business logic is decoupled from the routing binding. We can provide the same service logic processing for different routes while maintaining differentiated treatment in traffic limiting and other aspects. We can also solve the legacy of API design — old apis that can be invoked on new business logic.
New division
-
The View dead
-
The Model is split into two layers, one for data entities and the other for data access.
-
Controllers are split into two layers, one responsible for binding routes and the other for business logic.