Development history of back end separation and front end modularization before software architecture

Time:2021-1-27

In the current software architecture, the front-end and back-end are separated, that is, the front-end only focuses on page rendering, while the back-end focuses on business logic. The front-end and back-end are two different types of work, and the most common way of front-end and back-end interaction is through the interface.

Front end and back end separation architecture

Before formally explaining the separation of front and back-end architecture, let’s take a look at the architecture pattern of traditional software development many years ago.

Why should the front and rear ends be separated

I still remember when I was in college in the last few years, when I was just learning java web development, the textbook introduced JSP + servlet, a very traditional architecture mode. At this time, the front-end and back-end business logic code were all in one project, and they were not separated. This development mode belongs to model1 Mode, although the separation of logic function and display function is realized, the view layer and control layer are realized by JSP pages, that is, the view layer and control layer are not separated.

With the deepening of learning and the gradually popular enterprise application development, we gradually abandon this technology selection, and began to use a number of open source frameworks in the project. The common framework combinations are spring + struts / spring MVC + Hibernate / mybatis Due to the advantages of the framework and good encapsulation, this set of development framework combination quickly becomes the only choice in the development of various enterprises. The emergence of these frameworks also reduces the repeated coding work of developers, simplifies the development, speeds up the development progress, and reduces the difficulty of maintenance. What is hot is the development mode behind this set of technical framework, namely MVC Development mode, which is designed to overcome the shortcomings of model1.

The specific meaning of MVC is: model + view + controller, that is, model + view + controller,

  • Model layer: it is often written with JavaBean. It accepts the data requested by the view layer, and then carries out the corresponding business processing and returns the final processing results. Its responsibility is the most core. It also makes use of the characteristics of JavaBean to realize the reuse and expansion of code and bring convenience to maintenance.
  • View layer: it represents the interface that interacts with users and is responsible for data collection and display, which is usually implemented by JSP.
  • Controller control layer: the control layer receives the request from the client, then passes the request to the model layer and tells the model layer what function module should be called to process the request. It will coordinate the work between the view layer and the model layer and play the role of the intermediate hub. It is generally implemented by servlet.

The workflow of MVC is shown in the figure below.

Development history of back end separation and front end modularization before software architecture
At the same time, project development will be divided into three layers: control layer, business layer and persistence layer. The control layer is responsible for receiving the parameters, calling the relevant business layer, encapsulating the data, routing and rendering the data to the JSP page, and then The data in the background will be displayed on the page. I believe you are very familiar with this development mode. Whether it is enterprise development or personal project construction, this development mode is the first choice for you. However, with the expansion of the development team and the continuous evolution of the project architecture, this development mode is gradually inadequate.

Next, let’s analyze the pain points of this development model.

Pain point 1: JSP efficiency

First of all, JSP must run in servlet container (such as tomcat, jetty, etc.). When JSP is requested, it also needs a compilation process, and finally it is translated into Java class and class file, which will occupy permgen space. At the same time, it also needs a new class loader to load. JSP technology is similar to Java language and servlet It has strong relevance and can’t compare with template engine or pure HTML page in decoupling. Secondly, the response after each JSP request is HTML page output by servlet through the output stream, and the efficiency is not as high as using HTML directly. Because of the strong association between JSP and servlet container, nginx can not be directly used as JSP Web server in project optimization, so the performance improvement is not high.

Pain point 2: unclear division of labor

In this development mode, the workflow is usually: after the designer gives the page prototype design, the front-end engineer is only responsible for cutting the design drawing into HTML pages, and then the back-end development engineer needs to convert the HTML into JSP pages for logical processing and data display. In this working mode, the human error rate is high, the back-end developers’ task is more heavy, when modifying the problem, both sides need to cooperate in the development, which is inefficient. Once there is a problem, the front-end developers are faced with JSP pages full of tags and expressions, and the back-end developers are not well versed in the front-end technology when facing the problem of style or interaction.

In some emergency situations, the front-end staff will debug the back-end code, and the back-end developers will debug the front-end code. The division of labor is not clear, and the communication cost is high. Once some functions need to be reworked, the front-end and back-end developers will be needed. In this case, it is also unfavorable for the later technical growth of the front-end and back-end staff. The back-end pursues high concurrency and high availability The front-end pursues modularization, component integration, speed, compatibility, user experience and so on. However, in the MVC development mode, it obviously has certain constraints on these technicians.

Pain point 3: not conducive to project iteration

At the beginning of the project, in order to quickly launch the application, we choose to use this development mode for Java Web The development of the project is a very correct choice. At this time, the traffic is small, the number of users is not high, and there will not be very harsh performance requirements. However, with the continuous growth of the project, the number of users and request pressure will continue to expand, and the performance requirements for Internet projects are higher and higher. If the front-end and back-end modules are still coupled together at this time, it is not conducive to subsequent expansion. For example, in order to improve the load capacity, we will choose to make clusters to share the pressure of a single application, but the coupling of modules will make the space for performance optimization lower and lower, because a single project will become larger and larger, and the best optimization can not be achieved without reasonable splitting, or when the version is deployed online, only the back-end code is changed, and the front-end also needs to be optimized To redistribute, or change only part of the page or part of the style, the back-end code also needs to be released online. These are common bad phenomena when the coupling is serious. Therefore, the original architecture mode of front-end and back-end coupling has gradually failed to meet the evolution direction of the project, so we need to find a decoupling way to replace the current development mode.

Pain point 4: not meeting business needs

With the continuous development of the company’s business, only the browser side of the web application has gradually become a little insufficient. At present, it is the era of rapid growth of the mobile Internet. The native app application on the mobile terminal has been very mature. With the popularity of APP software, more and more enterprises have joined the app In software development, in order to seize business opportunities and improve user experience as much as possible, your company may not put all development resources on Web applications, but develop multi terminal applications at the same time. At this time, the company’s business line may be as follows or part of them:

Development history of back end separation and front end modularization before software architecture

Browser side web applications, IOS native apps, Android native apps, wechat applets and so on may only be part of the development of products, but except that web applications can be developed using the traditional MVC mode, others can not be developed using this mode, such as native apps or wechat applets are developed by calling restful API Data interaction with the back-end is realized in the same way.

With the development of Internet technology, more and more technical frameworks have been proposed, and the most revolutionary one is the concept of front-end and back-end separation.

What is front and rear end separation

What is the separation of front and back ends? I think we should understand it from the following aspects.

Front end and back end separation is a project development mode

When the business becomes more and more complex or the product line is more and more, the original development mode has been unable to meet the business needs, when there are more and more products on the end, the change of presentation layer is faster and faster, more and more. At this time, we should separate the front end from the back end and abstract it hierarchically to simplify the data acquisition process. For example, the front-end staff is commonly used to realize jump logic and page The back end is only responsible for providing interface data, and the two interact with each other by calling restful API, as shown in the figure below:

Development history of back end separation and front end modularization before software architecture
At this time, there will be no need to turn HTML code into JSP for development. The front-end project is only responsible for the front-end part, and will not mix with any back-end code. In this way, the code is no longer coupled. At the same time, the front-end project and the back-end project will no longer be seriously coupled. As long as the front-end and back-end negotiate and define the interface specification and data interaction specification, the two sides can develop in parallel without interference, and the business will not be coupled. The two sides only interact through the interface.

In MVC When developing a project, the back end is often too heavy, and the “control” is relatively large. It is not only responsible for dealing with back-end operations such as business logic and permission management, but also needs to deal with logic such as page Jump. In the mode of front-end and back-end separation, the back-end has changed from the dictator of the former to the interface provider, and the front-end has not only dealt with a small part of the business as before, but also the page Jump Face jump is no longer handled and decided by the back end. The control of the whole project has been transferred from the back end to the front end, and the front end needs to handle more.

The front-end project and the back-end project are separated from each other and do not interfere with each other. The functional requirements of the project are completed through the interface and data specification, which is also a popular development method at present.

The separation of front and back ends is a division of labor

In the architecture mode of front-end and back-end separation, the back-end is responsible for providing data, and the front-end is responsible for displaying and interacting. In this development mode, the division of responsibilities between front-end developers and back-end developers is clear, and each side performs its own duties. There is no place with unclear boundary, and the employees also perform their own duties.

Front end developers include web developers and native app developers, while back-end developers refer to Java developers (take Java language as an example). Different developers only need to pay attention to the projects they are responsible for. The back end focuses on the control layer (restful API), service layer and data access layer, while the front end focuses on the front-end control layer and view layer. There will be no unclear responsibilities and front-end coupling such as front-end personnel need to maintain part of the back-end code, or back-end developers need to debug styles. Let’s compare the two project development process diagrams

Development history of back end separation and front end modularization before software architecture
At this time, there will be front-end and back-end coupling in the development process. If there is a problem, the front-end needs to be reworked, and the back-end also needs to be reworked, the development efficiency will be affected. Now, the flow chart is as follows:

Development history of back end separation and front end modularization before software architecture
After the separation of front-end and back-end, the server-side developers and front-end developers do their own work, and they do not interfere with each other,. After the completion of the design, web developers, app developers and back-end developers can all participate in the development work, which can achieve parallel development. The responsibilities of front-end developers and back-end developers are separated. Even if there is a problem, it is also to repair their own problems, which will not affect and couple with each other. The development efficiency is high and meets the development needs of enterprises for multiple product lines.

Front end separation is an architecture pattern

After the separation of front end and back end, the application at each end can be packaged and deployed independently, and the deployment mode can be optimized accordingly. It is no longer a unified project at the front end and back end, and finally it can be deployed in a deployment package. Take the web application as an example. After the front-end project is deployed, it no longer depends on the servlet container. Instead, it can use the nginx server with higher throughput. The dynamic and static separation deployment mode is adopted, which not only improves the front-end access experience, but also reduces the pressure on the back-end server. If it is further optimized, it can use page cache, browser cache and other settings, or use CDN And other products to improve the access efficiency of static resources. For back-end services, cluster deployment can be carried out to improve the response efficiency of services, and service-oriented splitting can also be further carried out. Independent deployment maintenance and targeted optimization after the separation of front and back end can speed up the overall response speed and throughput.

Front end development history

When we go to understand something, first of all, we need to understand its history, in order to better grasp its future.

primeval ages

Ncsamosaic, the first browser in the world, was developed by Netscape in 1994. Its original intention is to facilitate scientific researchers to access information and documents (most of the documents at this time are in the form of pictures). In that era, every interaction, button click, form submission, had to wait a long time for the browser to respond, and then download a new page again.

In the same year, PHP (Hypertext preprocessor) scripting language was developed, which opened the MVC mode of data embedding template. At the same time, there are several similar practices

  • PHP embeds data directly into HTML.
  • ASP ASPX, embedded in HTML C code.
  • Java JSP embeds data into web pages directly.

In this period, the majority of browser developers are the back-end developers. Most of the front-end and back-end developers are integrated. The general development process is: the back-end receives the request from the browser — sends the static page — sends it to the browser. Even if there is a special front-end development, it just uses HTML to write page template and CSS to arrange a better layout for the page. In this period, the role of the front end was limited, and it was often just the role of the cutter.

Iron Age

In 1995, a boss of Netscape, Brandon edge, wanted to develop a Java like scripting language to improve the display effect of browsers and enhance the dynamic interaction ability. As a result, the boss was drinking beer and smoking cigarettes, and he wrote this scripting language in ten days. It’s very powerful, but the syntax is not like Java at all. This gradually formed the prototype of the front end: HTML as the skeleton, CSS as the appearance, JavaScript as the interaction.

At the same time, some companies such as Microsoft also developed their own script language for their own browsers. There are many kinds of browsers. Although there is a relatively unified ECMA standard, browsers are popular in the market before the standard and become the de facto standard. As a result, now the front-end engineers have to deal with browser compatibility (evil ie Series) while doing some government projects.

In any case, the front-end development can write some logic code, and is no longer the low-end development that can only draw pages. With the emergence of Ajax in 1998, the front-end development has changed from Web1.0 to Web2.0. The front-end development has changed from static display of pure content to a new era of dynamic web page, rich interaction and front-end data processing. During this period, two well-known browser products rich in interactive dynamics were.

  • Gmail (2004)
  • Google Maps (2005)

Due to the increasing demand for dynamic interaction and data interaction, an excellent cross browser JS tool library such as jQuery (2006) has been derived, which is mainly used for DOM operation and data interaction. Some old projects, even large-scale projects developed in recent years, are still using jQuery, so that the jQuery library is still being updated, although the volume is far less than the excellent front-end libraries such as react and Vue.

Information Age

Since 2003, the front-end development has gone through a relatively stable period, and the major browser manufacturers have not done anything else except to update their browser products step by step. But we programmers can’t bear loneliness. Industrialization has promoted the rapid arrival of informatization. The amount of data presented by browsers is increasing, and the demand for dynamic interaction of web pages is increasing. JavaScript operates dom Is too laggy and the bottleneck becomes more and more obvious (frequent interaction operation, which leads to page’s very bad). It’s becoming more and more difficult to improve the page performance only from the code level. So the good guys did some earth shaking things:

  • In 2008, Google V8 engine was released, ending the era of Microsoft ie.
  • In 2009, angularjs and node were born.
  • Reactjs was born in 2011.
  • Vuejs was born in 2014.

Among them, V8 and Node.JS With the emergence of JavaScript, front-end developers can write back-end system with familiar syntax sugar, and provide the front-end with the opportunity to use the same language to realize full stack development (JavaScript is no longer a script language that can only write page interaction). With the emergence of MVVM front-end frameworks such as react, angular and Vue, the front-end realizes the real application of the project (SPA single page application), no longer relies on the background developers to process the page routing controller, and realizes the self-management of page Jump. At the same time, it also promotes the complete separation of the front-end and the back-end (the front-end projects are deployed independently and no longer rely on the similar template file directory).

As for why MVVM framework can improve the rendering performance of the front-end, let’s briefly talk about the principle, because a large number of DOM operations are the main culprit of the performance bottleneck. Through certain analysis and comparison algorithms, it is feasible to achieve the minimum DOM overhead under the same effect. React, Vue and other frameworks are mostly realized through this kind of thinking, and the specific implementation can be seen in the relevant materials. The separation of the front end and the back end also leads to some changes in the division of labor of the front end.

Development history of back end separation and front end modularization before software architecture

The back-end development pays more attention to data services, while the front-end is responsible for presentation and interaction. Of course, the corresponding learning costs are also increasing, Node.JS The emergence of front-end also makes it possible to develop front-end, front-end and back-end together. Many big companies have tried to develop the front-end together around 2015 Node.JS As an intermediate data transfer layer, the back end is more focused on data service and governance.

Development of front end modularization

Since May 2009 Node.js Since its release, the front end has been able to do more and more things. In just 10 years, the front end has moved from the era of slash and burn cultivation to the era of modularization and engineering. All kinds of front-end frameworks contend, and the front-end wins its own era.

primeval ages

Back in 2009, I remember that there was no popular separation of front-end and back-end at that time, many projects were still mixed together, and most of the front-end developers at that time were “cut map”. The front-end completes the static page, and the server colleagues complete the data embedding, which is the so-called set page operation. Whenever there is a similar function, they will return to the previous page to copy and paste. Because they are in different pages, the class name needs to be changed, but the soup does not change.

Over time, there are more and more duplicate codes. If you change a small place, you need to change a lot of code, which is very inconvenient and not conducive to large-scale engineering development. Although excellent front-end frameworks such as angular and Avalon are gradually emerging in the market, considering that SEO and maintenance personnel are not easy to recruit, many companies still choose to seek stability and make web pages in the form of a set of pages, which is a big obstacle to the engineering and modularization of the front-end.

The emergence of building tools

However, with node being highly respected, a large number of building tools have emerged in the market, such as NPM scripts, grunt, gulp, FIS, webpack, rollup, parcel and so on. Building tools liberate our hands and help us deal with some repetitive mechanical work.

Take a simple example: we write a piece of code in ES6, which needs to be executed in the browser. However, because the browser manufacturers are very conservative in updating the browser, a lot of ES6 code can not run directly on the browser. At this time, we can’t manually change ES6 code to Es5 code. So there is the following transformation.

//Before Compilation
[1,2,3].map(item => console.log(item))
//After compilation
[1, 2, 3].map(function (item) {
  return console.log(item);
});
//After code compression
[1,2,3].map(function(a){return console.log(a)});

Only by doing the above operations can we use the latest ECMAScript syntax when writing the front-end code, and compress the code volume as much as possible, so that the browser can load static scripts more quickly.

Traditional modularization

With the popularity of Ajax, what front-end engineers can do is not just “cut diagram”. Now front-end engineers can do more and more, there is a clear division of labor, and they can carry out data joint debugging with service engineers. The traditional modularization here is not the post-modern modularization. The early modularization did not use any tools. It was just JavaScript to complete the code structure. In the traditional modularization, we mainly extract some reusable code into common methods for unified maintenance and management, such as the following code.

function show(id) {
  document.getElementById(id).setAttribute('style', "display: block")
}
function hide(id) {
  document.getElementById(id).setAttribute('style', "display: none")
}

Then, we encapsulate these tool functions into a JS script file and introduce them where we need to use them.

<script scr="./utils.js"></script>

However, this practice will lead to two major problems, one is the pollution of global variables, the other is that manual maintenance of the dependency relationship between modules will cause code confusion.

For example, when our project is maintained by more than ten or even dozens of people, it is inevitable that someone will add new methods to the common components, such as show. Once the method is overridden, the people who use it will get different results than expected, resulting in the pollution of global variables. Another problem is that the dependency relationship between public scripts in real projects is quite complex. For example, C script depends on B script, and a script depends on B script. We should pay attention to this when we introduce it.

<script scr="c.js"></script>
<script scr="b.js"></script>
<script scr="a.js"></script>

In order to ensure the normal operation of a script, an error will be reported. How can we solve such problems?

Pollution of global variables

There are two ways to solve this problem. First, let’s talk about the method of treating the symptoms but not the root cause. We develop documents through the team specification. For example, I have a method that is used in the shopping cart module, which can be written as follows.

var shop.cart.utils = {
  show: function(id) {
    document.getElementById(id).setAttribute('style', "display: block")
  },
  hide: function(id) {
    document.getElementById(id).setAttribute('style', "display: none")
  }
}

In this way, we can effectively avoid the pollution of global variables, write methods to objects, and then call them through objects. In technical terms, this is called the specification of the namespace, but if there are too many modules, the variable names will be cumbersome. Once written, it will be a long string, so I call it a temporary solution rather than a permanent solution.

There is also a more professional method and technology to encapsulate closures by executing functions immediately. In order to solve the problem of encapsulating internal variables, executing functions immediately is a good method, which is also a way that many early developers are using, as shown below.

(function() { 
   var Cart = Cart || {};
   function show (id) {
     document.getElementById(id).setAttribute('style', "display: block")
   }
   function hide (id) {
     document.getElementById(id).setAttribute('style', "display: none")
   }
   Cart.Util = {
     show: show,
     hide: hide
   }
})();

The above code, through an immediate execution function, gives the independent scope of the module, and configures our module through global variables, so as to achieve the purpose of modularization.

Current modular solutions

Let’s talk about the commonjs specification first. In the Node.JS After the release, the commonjs modular specification has been used in project development. It has several concepts to explain.

  • Each file is a module, which has its own scope. The variables and functions defined inside are private and invisible to the outside;
  • The module variable inside each module represents the current module, which is an object;
  • The exports attribute of a module is an external interface. To load a module is to load the module module.exports Attribute;
  • Use the require keyword to load the corresponding module. The basic function of require is to read and execute a JavaScript file, and then return the exports object of the modified module. If not, an error will be reported;

Let’s take a look at the example. We will modularize the code mentioned above through commonjs.

module.exports = {
  show: function (id) {
    document.getElementById(id).setAttribute('style', "display: block")
  },
  hide: function (id) {
    document.getElementById(id).setAttribute('style', "display: none")
  }
}
//You can also output a single method
module.exports.show = function (id) {
  document.getElementById(id).setAttribute('style', "display: block")
}

//How to introduce
var utils = require('./utils')
//Use it
utils.show("body")

In addition to the commonjs specification, there are several modular patterns that can only be seen in old projects, such as require.js For the representative of AMD (asynchronous module definition) specification and Yubo team sea.js Is the representative of the CMD (common module definition) specification.
Amd features: one step loading module, but the premise is to load all the dependencies completely at the beginning. The characteristic of CMD is: it depends on delay and loads when needed.

AMD

First, let’s take a look at how to pass the AMD specification require.js Write the above modular code.

define(['home'], function(){
  function show(id) {
    document.getElementById(id).setAttribute('style', "display: block")
  }
    function hide(id) {
    document.getElementById(id).setAttribute('style', "display: none")
  }
  return {
    show: show,
    hide: hide
  };
});

//Load module
require(['utils'], function (cart){
  cart.show('body');
});

require.js This paper defines a function define, which is a global variable and is used to define modules. Its syntax specification is as follows:

define(id, dependencies, factory)

  • ID: it is an optional parameter used to identify the module;
  • Dependencies: the array of module names that the current module depends on. For example, the above module depends on the home module, which solves the problem of disordered dependency relationship between modules mentioned before. The pre dependent module can be loaded through this parameter;
  • Factory: the module initializes the function or object to be executed.

require([dependencies], function(){})

Then, use require to import in other files. The first parameter is an array of dependent modules, and the second parameter is a callback function. When the previous dependent module is loaded successfully, the callback function will be executed, and the loaded module will be passed into the function as a parameter for other operations.

CMD

sea.js And require.js In fact, the problem to be solved is the same, but the operation mechanism is different. We follow the proximity dependence. Let’s take a look at the modular code implemented by using CMD.

define(function(require, exports, module) {
  function show(id) {
    document.getElementById(id).setAttribute('style', "display: block")
  }
  exports.show = show
});

<script type="text/javascript"></script>
<script type="text/javascript">
  //Introduction module through seajs.use Then you can use the method exported by the module above in the callback function
  seajs.use('./utils.js',function (show) {
        show('#box');
  }); 
</script>

The first is introduction sea.js Library, definition module and export module are define() and exports, respectively. When defining a module, you can manually import the module you need to rely on through the require parameter, and use the module to seajs.use .

ES6

ES6 puts forward the latest modularization scheme, and introduces the class mechanism, which makes JavaScript transform from the early form verification script language into an object-oriented language. The modularization of ES6 uses the import / export keyword to implement import and export, and automatically adopts the use strict mode. Considering that they all run in modules, ES6 actually upgrades the whole language to the strict mode.

In ES6, every module is a file. Variables, functions and objects defined in the file cannot be obtained from outside. If you want to get the content in the module, you must use the export keyword to expose it. Let’s refactor the previous public script in the form of ES6.

// utils.js
const show = () => {
  document.getElementById(id).setAttribute('style', 'display: block');
}
const hide = () => {
  document.getElementById(id).setAttribute('style', 'display: none');
}

export {
    show,
  hide
}
//Or throw the method directly
export const show = (id) => {
  document.getElementById(id).setAttribute('style', 'display: block');
}
export const hide = (id) => {
  document.getElementById(id).setAttribute('style', 'display: none');
}

//External introduction module
import { show, hide } from './utils'

It can be found that ES6 is written more clearly. With the characteristics of object-oriented and function oriented, readability is stronger.