Pattern system and simplest Node.js MVC Web Server design

  javascript, node.js

I have been studying the design pattern for so long and have been watching it recently.Node.jsI have been wondering why there is such a thing as mode, so what is mode? After reading “Pattern Oriented Software Architecture”, I slowly realized that I had some system concepts.

What is the pattern?

When faced with a specific problem, experts seldom look for a new solution that is completely different from the existing one, but often think of a similar problem that has been solved before and apply the essence of its solution to solve this new problem.

Patterns can be obtained by abstracting common factors from specific problems-solutions: these problems-solutions are usually a series of familiar problems and solutions, and each pair of problems-solutions presents the same pattern.

Model-View-Controller mode

MVCPatterns are widely used in modern software development processes. WhyMVCThe existence of patterns can be seen in this example: developing software with human-computer interface.

User interface requirements can easily change. For example, when adding application functions, you must modify the menu to be able to access the new functions, and you may need to adjust the user interface for specific customers. The system may need to be migrated to another platform, which uses a completely different “look and feel” standard. Even if you upgrade to a new version of the Windows system, you may need to modify the code. In a word, if the service life of the system is very long, it may often be necessary to modify the user interface. When designing a flexible system, making the user interface and the function core closely intertwined will pay a high price and be prone to errors. As a result, it may be necessary to develop and maintain a number of very different software systems-one for each user interface, and modifications will involve many different modules. In short, when developing this interactive software system, the following two aspects must be considered:

  • Should be able to easily modify the user interface, can be completed in the run phase;
  • When adjusting or porting the user interface, the code at the core of the application function should not be affected.

To solve this problem, interactive applications should be divided into three parts: processing, output and input.

  • Model (model) Components encapsulate core data and functions, independent of output representation and input behavior.
  • View (view) component displays information to the user. The view obtains the information it displays from the model. A model can

There are multiple views.

  • Each view has an associated controller (controller) component. The controller accepts input, usually an event indicating mouse movement, mouse button activation, or keyboard input. Events are converted into service requests, which are either sent to the model or to the view. The user only interacts with the system through the controller.

By separating the model from the view and controller components, the same model can have multiple views. If the user modifies the model through the controller of one view, the change should be reflected in all other views that depend on relevant data. For this reason, whenever the data of the model changes, it will notify all views, and the views will retrieve new data from the model and update the displayed information. This solution ensures that modifying one subsystem of the application will not seriously affect the other subsystems. For example, the non-graphical user interface can be changed into a graphical user interface without modifying the model subsystem, and new input devices can be supported without affecting the display of information and the functional core. All software versions can rely on the same model subsystem, which is independent of “appearance.”

Implement an authentication service with Model-View-Controller mode.

Let’s start with the structure shown in the following figure:

The above figure showsModel-View-Controller modeA typical example of; It describes the structure of a simple authentication service.AuthControllerAccept input from the client, extract login information from the request, and perform some preliminary verification. AfterAuthServiceCheck whether the certificate provided by the client matches the information stored in the database; Last usedbThe module performs some specific queries as a means of communicating with the database. The way these three components are connected together will determine the reusability, testability and maintainability of the application.

Here: Model (Model) refers todbModule, controller (Controller) refers toAuthControllerAndAuthServiceAnd the view is the front-end user interface, that isHTMLDocuments.

The most natural way to connect these components together is throughAuthServiceRequestdbModule, and then fromAuthControllerRequestAuthService.

Let’s demonstrate this by actually implementing the system just described. Then let’s design a simple authentication server, it will have the following twoHTTP API

  • POST '/ login': receives the that contains the user name and password pair for authenticationJSONObject. On success, it returns aJSON Web Token(JWT), which is used in subsequent requests to verify the user’s identity.
  • GET'/ checkToken': check whether the user has permissions.

For this example, we will use several techniques; This is no stranger to us. We useexpressTo achieveWeb APIAndlevelupTo store user data.

Db module

Let’s start building applications from the bottom up. First of alllevelUpModules for database instances. Let’s create a project calledlib/db.js, which contains the following contents:

const level = require('level');
const sublevel = require('level-sublevel');
module.exports = sublevel(
  level('example-db', {
    valueEncoding: 'json'
  })
);

The previous module is stored in the./example-dbIn the catalogLevelDBDatabase connection, and then use thesublevelTo modify the example, through this module to achieve the addition, deletion, query and modification of the database. The object exported by the module is the database object itself.

AuthService module

Now we have itdbOne example, we can use it to achievelib/authService.jsModule, which is responsible for querying the database and checking whether the user has permissions according to the user’s identity credentials. The code is as follows (only relevant parts are shown):

"use strict";

const jwt = require('jwt-simple');
const bcrypt = require('bcrypt');

const db = require('./db');
const users = db.sublevel('users');

const tokenSecret = 'SHHH!';

exports.login = (username, password, callback) => {
  users.get(username, (err, user) => {
    if(err) return callback(err);
    
    bcrypt.compare(password, user.hash, (err, res) => {
      if(err) return callback(err);
      if(!res) return callback(new Error('Invalid password'));
      
      let token = jwt.encode({
        username: username,
        expire: Date.now() + (1000 * 60 * 60) //1 hour
      }, tokenSecret);
      
      callback(null, token);
    });
  });
};

exports.checkToken = (token, callback) => {
  let userData;
  try {
    //jwt.decode will throw if the token is invalid
    userData = jwt.decode(token, tokenSecret);
    if (userData.expire <= Date.now()) {
      throw new Error('Token expired');
    }
  } catch(err) {
    return process.nextTick(callback.bind(null, err));
  }
    
  users.get(userData.username, (err, user) => {
    if (err) return callback(err);
    callback(null, {username: userData.username});
  });
};

authServiceModule implementationlogin()Service, which is responsible for querying the database, checking user name and password information,checkToken()Service acceptancetokenAs a parameter and verify its effectiveness.

AuthController module

Continuing at the application level, we will now look atlib/authController.jsModules. This module is responsible for handlingHTTPRequest, it is essentiallyExpressA collection of routes; The code of this module is as follows:

"use strict";

const authService = require('./authService');

exports.login = (req, res, next) => {
  authService.login(req.body.username, req.body.password,
    (err, result) => {
      if (err) {
        return res.status(401).send({
          ok: false,
          error: 'Invalid username/password'
        });
      }
      res.status(200).send({ok: true, token: result});
    }
  );
};

exports.checkToken = (req, res, next) => {
  authService.checkToken(req.query.token,
    (err, result) => {
      if (err) {
        return res.status(401).send({
          ok: false,
          error: 'Token is invalid or expired'  
        });
      }
      res.status(200).send({ok: 'true', user: result});
    }
  );
};

authControllerThe module implements twoExpressRouting:login()Used to perform login operation and return the correspondingtoken,checkToken()For inspectiontokenThe effectiveness of. These two routes delegate most of their logic toauthService, so their only job is to deal withHTTPRequests and responses.

App module

Finally, at the entry point of the application, we call ourcontroller. Following the agreement, we will put this logic in the name ofapp.jsIn the module of, under the root directory of our project, as shown below:

"use strict";

const Express = require('express');
const bodyParser = require('body-parser');
const errorHandler = require('errorhandler');
const http = require('http');

const authController = require('./lib/authController');

let app = module.exports = new Express();
app.use(bodyParser.json());

app.post('/login', authController.login);
app.get('/checkToken', authController.checkToken);

app.use(errorHandler());
http.createServer(app).listen(3000, () => {
  console.log('Express server started');
});

We can see that our application modules are very basic. It contains a simpleExpressServer, which registers some middleware andauthControllerTwo routes exported. This is a simple inclusioncontrollerAndmodelTheWebService, Add Front EndHTMLPage, also realizedMVCSeparation of Architecture

Characteristics of patterns

  • The model describes the problems that occur repeatedly in specific design situations and provides solutions.
  • The model records the existing design experience that has been fully proved.
  • Patterns describe abstractions that transcend classes, instances, and components.
  • The pattern provides a common language and allows everyone to have a consistent understanding of design principles.
  • Patterns are a means of recording software architecture.
  • Patterns help create software with specified characteristics.
  • Patterns help to create complex and heterogeneous software architectures.
  • Patterns help control the complexity of software.

Why is it called mode

Each mode consists of three parts:

  • Background (ContextThe background of the problem;
  • Question (Problem) The recurring problems under this background;
  • Solution (Solution) The solution tested by practice.

Background

The background depicts the situation of the problem, which enriches the original ordinary problem-the solution. The background of the mode may be very general, such as “developing software with human-computer interface”, or it may link specific modes together, such as “implementing change propagation mechanism among models, views and controllers”.

Problem

This part of the schema describes the recurring problems in the given context. It starts with a general statement of the problem, explaining the essence of the problem: what are the specific design problems that must be solved? For example,Model-View-ControllerThe mode solves the problem that the user interface changes frequently. Patterns represent all aspects to consider when solving problems:

  • The solution must meet the needs, such as the peer-to-peer communication between processes must be efficient;
  • Constraints that must be considered, such as inter-process communication must comply with specific protocols;
  • Features that a solution must have, such as the ability to easily modify software.

Model-View-ControllerThe model illustrates two forces: modifying the user interface should be easy, and such modification should not affect the core functions of the software.

Solution

The solution part of the model points out how to solve the recurring problems and more precisely how to balance the relevant roles.
Force. In the software architecture, such a solution includes two aspects:

  • Each pattern specifies a specific structure, that is, the spatial configuration of elements. For example,Model-View-ControllerThere is a sentence in the description of the pattern: “divide the interactive application into three parts-processing, output and input.”
  • Each mode describes the behavior of the runtime. For example, inModel-View-ControllerThe “solution” section of the mode has the following sentence: “the controller accepts input, which is usually an event indicating mouse movement, mouse button activation, or keyboard input. Events are converted into service requests, which are either sent to the model or to the view. “

Type of pattern

Patterns are generally divided into three categories:

  • Architecture pattern: the template of specific software architecture, which depicts the system-level structural features of application programs and will affect the architecture of subsystems. For exampleModel-View-ControllerMode
  • Design pattern: It is a medium-sized pattern, smaller in scale than architecture pattern, but usually independent of programming language and programming paradigm. Application design patterns will not affect the basic architecture of softw are systems, but may seriously affect the architecture of subsystems. For example, observer mode.
  • Example: how to solve specific design problems. Patterns specific to a particular language. For exampleC++linguisticCounted BodyMode.

Summary

The pattern provides a promising method for developing software with specified characteristics. They record the existing design knowledge and help to find a proper solution to the design problems. Patterns vary in scale and abstraction, covering many important areas of software development. Patterns are interwoven with each other. We can use one pattern to improve another larger pattern, and we can also use multiple patterns to solve complex problems. The model discusses some important aspects of software architecture and provides supplements to existing technologies and methods. Patterns can be used in combination with any programming paradigm and can be implemented in almost any programming language.