This is the second part of the back-end developer’s series on building a mobile application from scratch. Introduce the next new project, how to build the back end from scratch. Let’s assume that the project consists of two parts
-
Apis provided for WAP sites and APPS;
-
A management background for use by operations personnel.
The entire project uses Phalcon, and a demo of the project can be seen here
Remark: Following the progress of the article, the project will continue to be updated, and finally it will be integrated with the supporting WAP APP
The project will eventually include at least the following:
-
Xiaomi Message Push
-
Payment integration (Alipay, China Merchants, wechat)
-
API testing based on Codeception
-
Login API (this part uses oauth2 and will be based on ‘bshaffer/ oAuth2-server-php ‘)
Project Structure Review
The back-end system generally adopts the MVC structure (here PHP is taken as an example), where M stands for model, V for view, and C for controller. I’m just saying a few words
Model refers to the data Model, which includes the table structure in your Mysql database, or the cache object structure in Redis. It represents a data manipulation unit.
View refers to an interface that is presented to the user for browsing and direct manipulation
Controller The Controller, primarily to separate the View from the Model, acts as a middleman, passing notes from one end to the other.
In my past projects, my main confusion was whether to put business logic in C or M.
From an object perspective, business logic is nothing more than manipulating data, either reading it or modifying it, so it should be in the M layer, because an object should have its own properties and methods.
The business is in M
In practice, we often have a scenario such as reading a game list data, which includes the details of the game and the version information and download information of the game. Because there are upgrades in game apps, there are multiple packages for one game. So there are at least two models
-
Game details model, including the name of the game, logo and other basic information
-
Game package information model, including package platform, size, download address, version information, etc
So where should the method of this action be encapsulated? The previous approach was to encapsulate the corresponding operations into the corresponding model and call them separately in the controller. Back to this, the game Model encapsulates the method that queries the list of games, and the package Model encapsulates the method that queries the package information based on the game ID.
We then call these two methods separately in the controller, and then assemble the package for the game into the corresponding game.
So here’s a question: suppose we need to return a collection of related games in the game details controller method, do we repeat the above operation again? Some might say that the processing of the game is separated into a public method, but what if it is called in the news detail? It’s not even supposed to be in the same controller!
The business is in C
So we had a little bit of trouble reusing our methods in model, so let’s go ahead and see what happens in Controller?
An advantage of this time is that we can use the connection query, the just 2 queries, through the connection query 1 complete, for mysql time reduced, program performance improved, and then the query results snap processing completed.
Well, without further ado, as I’m sure you’ve already noticed, this query process is still not reusable. Naturally, it should be thought of here that it can be distilled into a method that other controllers can’t use (the idea of one controller calling another controller is out of the question). Then you have to refine it into a single class that encapsulates all the business.
Then, wherever we need the game list data, we call the GameServer directly (assuming the encapsulated business logic is in xxxServer) and get the same data, and if the business changes, we only need to change this one, and the data will be the same everywhere.
So by reviewing, we concluded that our back-end project needed a server hierarchy to house the business logic.
The meaning of the Server layer
The separate layer covers all business functions centrally and greatly improves the reuse of code. In addition to the direct use of different methods of different controllers, it also includes the reuse between different modules.
But there are a few extra things that the Server layer needs to consider before taking the different modules, like we have an APP API module, we have a back-end management module. Then it is all about obtaining list data, and some fields may not be needed for APP API module, but the background management needs to know all the content and some problems on background user permissions. These parts can continue to be split and combined with the server. It needs to be managed in combination with its own business.
Another benefit of code in my own practice is that the Server layer simplifies the C layer in a way that allows new people on the team to pick up the code quickly. For example, Ming is new to the team, so if he is familiar with the framework he uses, he can immediately start doing things in layer C, because there is no business here, only the validation of the data passed by the client and the return of the call to the Server layer. This process can speed up their integration into the team.
Uniform return format
The convention of the format of the data returned by the API is basically the first step in the development of the system, which is usually passed through each controller
return json_encode([
'msg'= >'ok'.// Carries information that can be used to alert users
'data'= > [// Specific data. . ] .'code'= >'0'.// 0 indicates success. Other values indicate errors
])Copy the code
So the first problem we’re going to have here, to make it easier for the front end to determine the type, is that basically all of the field values are going to be returned as strings. The contents of data need to be handled in each controller for strings, UTF-8 encoding, and so on. To duplicate code, even if you pull out a method, you have to face this problem. A good solution is to do uniform processing within interceptors that return data (every framework has a similar concept).
Additional problems with code like this may include mistyping field names, such as “code” as cdoe and “data” as date. Additional risk for program code (especially for bug fixes)
One solution would be to normalize the returned data structures in the form of objects. For example, we define a class:
class ResultData {
/** * Return information *@var string $msg
*/
private $msg;
/** * Return data structure *@var array|object|string
*/
private $data;
/** * API status code *@var int $apiCode
* @see ApiCode
*/
private $apiCode;
public function __construct(int $apiCode, string $msg = 'ok', $data = null)
{
$this->apiCode = strval($apiCode);
$this->msg = trim(strval($msg));
$this->data = $data;
}
/** * Get data result *@return array
*/
public function getRetData(a)
{
if (! is_array($this->data) && is_object($this->data) && method_exists($this->data, 'toArray')) {
$this->data = $this->data->toArray();
}
// valueToString Converts the value of data to string and utF-8 transcoding
$result = [
'code'= >$this->apiCode,
'msg'= >$this->msg,
'data'= >$this->data ? ArrayUtil::valueToString($this->data) : [],
];
if (! APP_ENV_PROD) {// The test environment displays API processing time information for optimization
$result['use_time'] = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
}
return$result; }}Copy the code
With the above class, all of our service layers or Controllers should use it as a return value. Then you can uniformly do JSON encode in the interceptor. In this way, the possibility of making mistakes is reduced. Meanwhile, the unified management of data processing places is centralized into ResultData, so that any special changes in the future will take effect everywhere.
Other problems
There are also questions about how oAuth2 is integrated into the project. This part is explained in the X-API project, which is superficial on paper.
Log recording is also a very important part of the system development, this part is not too much to say, with a standard format, store specified data (media can be: DB, file).
In system development, we should refuse to use var_dump and echo for debugging. In addition, we suggest using PhpStorm IDE for system development.
Subsequent to share
Next, you’ll complete the basic structure of an X-API, complete the PHP automated test section documentation tutorial, and then wrap up the back-end section. (This series of sharing is mainly at the code level, not related to system deployment issues)
GitHub:github.com/helei112g