編者注:作者是網(wǎng)易高級前端@季含婷,當(dāng)前負(fù)責(zé)網(wǎng)易供應(yīng)商業(yè)務(wù)線前端開發(fā)以及接口管理平臺建設(shè)工作亡资,專注于中后臺前端以及Node應(yīng)用開發(fā)建設(shè)工作澜共。
Node項(xiàng)目不會(huì)代碼分層怎么辦?來看看網(wǎng)易大神怎么做
一锥腻、背景
剛剛接觸Node開發(fā)或者后端開發(fā)的同學(xué)嗦董,有時(shí)候雖然能跟著網(wǎng)上的例子能去寫出一個(gè)Node應(yīng)用,但是網(wǎng)上的大神們或者項(xiàng)目領(lǐng)導(dǎo)說你對你的代碼分層了嗎瘦黑?
其實(shí)作為一個(gè)有追求的開發(fā)京革,自己也很想寫出更健壯,更易維護(hù)性的大型項(xiàng)目幸斥,而不是網(wǎng)上demo級別的例子匹摇。
那么我們應(yīng)該怎么做呢?
那這邊就跟大家分享一下我們是如何通過代碼分層甲葬,來提高項(xiàng)目的檔次的廊勃。
二、分層方案
本文主要介紹項(xiàng)目中代碼的分層結(jié)構(gòu)经窖,大致分為Dao坡垫、Model、Service画侣、Controller層冰悠,這種分層模式在Java、Android和IOS都有被應(yīng)用棉钧,下面我們看看在node中的應(yīng)用屿脐。
項(xiàng)目配置
編程語言:NodeJS
數(shù)據(jù)庫:mysql
第三方庫:@tiger系列包+Sequelize
項(xiàng)目目錄結(jié)構(gòu)
2.1 Dao層
一般Dao層是用來和底層數(shù)據(jù)庫通信涕蚤,負(fù)責(zé)對數(shù)據(jù)庫的增刪改查。
在項(xiàng)目中用了第三方庫Sequelize的诵,它是一個(gè)基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, SQLite 和 Microsoft SQL Server. 它具有強(qiáng)大的事務(wù)支持, 關(guān)聯(lián)關(guān)系, 讀取和復(fù)制等功能.
創(chuàng)建一個(gè)sequelize對象實(shí)例
2.2 Model層
Model即模型,常常和持久化的數(shù)據(jù)一一對應(yīng)万栅,Model承載的作用就是數(shù)據(jù)的抽象,描述了一個(gè)數(shù)據(jù)的定義西疤,Model的實(shí)例就是一組組的數(shù)據(jù)烦粒。
抽取model基類:BaseModel
基類BaseModel包含一個(gè)model對象以及對應(yīng)模型的CRUD操作,為繼承它的子類提供數(shù)據(jù)庫增刪改查的操作方法
import?{?sequelize?}?from?'../dao';
export?class?BaseModel?{
????model:?any;
????constructor(modelName:?string,?schema:?any,?option?:?any)?{
????????this.model?=?sequelize.define(modelName,?schema,?option);
????}
????//?返回實(shí)例化的sequelize模型實(shí)例
????getModel()?{
????????return?this.model;
????}
????//?多條查詢
????findAll(option:?any)?{
????????return?this.model.findAll(option);
????}
????//?單條數(shù)據(jù)查詢
????findOne(option:?any)?{
????????return?this.model.findOne(option);
????}
????//?更新方法
????update(values:?any,?option:?any)?{
????????return?this.model.update(values,?option);
????}
????//?刪除
????delete(option:?any)?{
????????return?this.model.destroy(option);
????}
????//?插入單個(gè)實(shí)體
????create(entity:?any)?{
????????return?this.model.create(entity);
????}
????//?批量插入實(shí)體集
????createBatch(entitys:?any)?{
????????return?this.model.bulkCreate(entitys);
????}
}
創(chuàng)建branch表映射model:BranchModel
BranchModel繼承基類BaseModel代赁,除了繼承了BaseModel的CURD方法外扰她,還可定義BranchModel自身需要的特殊方法。
import?Sequelize?from?'sequelize';
import?{?Service?}?from?'@tiger/boot';
import?{?BaseModel?}?from?'./base.model';
@Service
export?class?BranchModel?extends?BaseModel?{
????constructor()?{
????????super('branch',?{
????????????id:?{?type:?Sequelize.BIGINT,?allowNull:?false,?autoIncrement:?true,?primaryKey:?true?},
????????????//?分支名
????????????branchName:?{?type:?Sequelize.STRING(128),?allowNull:?false,?defaultValue:?''?},
????????????//?服務(wù)Id
????????????serviceId:?{?type:?Sequelize.BIGINT,?allowNull:?false,?defaultValue:?0?},
????????????//?分支描述
????????????description:?{?type:?Sequelize.STRING(4096),?allowNull:?false,?defaultValue:?''?},
????????????//?分支創(chuàng)建人的郵箱
????????????createUser:?{?type:?Sequelize.STRING(128),?allowNull:?false,?defaultValue:?''?},
????????????...
????????},?{?
????????????timestamps:?false,?
????????????tableName:?'TB_YX_API_BRANCH'?
????????});
????????this.model?=?super.getModel();
????????//?同步當(dāng)前模型到數(shù)據(jù)庫中
????????this.model.sync();
????}
????//?特殊方法定義
????batchUpdate(){
????????//?...
????}
}
2.3 Service層
Service的重點(diǎn)是在于提供服務(wù)芭碍,可以處理事務(wù)和業(yè)務(wù)邏輯徒役。
創(chuàng)建BranchModel對應(yīng)的service:BranchService
BranchService主要提供BranchModel的數(shù)據(jù)處理服務(wù);一個(gè)Model最好有一個(gè)與之對應(yīng)的service窖壕,這個(gè)service包含了業(yè)務(wù)需要對model處理的所有操作忧勿。
import?{?Service?}?from?'@tiger/boot';
import?{?BranchModel?}?from?'../../model/branch.model';
import?{?BranchInfoVO,?BranchInfoParamsVO,?CreateBranchInfoParamsVO,?SetBranchStateParamsVO?}?from?'./vo/branch-info.vo';
import?{?OpenIdInfo?}?from?'../shared/types';
@Service
export?class?BranchService?{
????constructor(
????????private?branchModel:?BranchModel
????)?{
????}
????async?getBranchList(serviceId:?number):?Promise<BranchInfoVO[]>?{
????????const?result?=?await?this.branchModel.findAll({
????????????where:?{
????????????????serviceId:?serviceId
????????????},
????????????order:?[['deleteFlag',?'ASC'],?['updateTime',?'DESC']]
????????});
????????return?result;
????}
????async?createBranch(param:?CreateBranchInfoParamsVO,?user:?OpenIdInfo):?Promise<any>?{
????????const?values?=?{
????????????...param,
????????????createUser:?user.email,
????????????createTime:?Date.now()
????????};
????????const?result?=?await?this.branchModel.create(values);
????????return?result;
????}
????async?updateBranch(param:?BranchInfoParamsVO,?user:?OpenIdInfo):?Promise<any>?{
????????const?values?=?{
????????????branchName:?param.branchName,
????????????description:?param.description,
????????????updateUser:?user.email,
????????????updateTime:?Date.now()
????????};
????????const?result?=?await?this.branchModel.update(values,?{?where:?{?id:?param.id?}?});
????????return?result;
????}
}
創(chuàng)建controller對應(yīng)的service:BranchManageService
BranchManageService提供與controller對應(yīng)的服務(wù),主要做業(yè)務(wù)邏輯處理瞻讽。一個(gè)controller最好有一個(gè)與之對應(yīng)的service鸳吸,這個(gè)service包含有controller調(diào)度的所有操作。
import?{?Service?}?from?'@tiger/boot';
import?{?BranchService?}?from?'./branch.service';
import?{?BranchApiDetailQueryVO,?ServiceBranchInfoListVO?}?from?'./vo/branch-info.vo';
@Service
export?class?BranchManageService?{
????constructor(
????????private?branchService:?BranchService
????)?{
????}
????//?批量查詢分支信息
????async?queryBranch(branchList:?BranchApiDetailQueryVO[]):?Promise<ServiceBranchInfoListVO[]>?{
????????const?serviceIdList:?number[]?=?branchList.map(item?=>?Number(item.serviceId)),
????????????branchIdList:?number[]?=?branchList.map(item?=>?item.branchId),
????????????result:?ServiceBranchInfoListVO[]?=?[],
????????????servicePromise?=?this.branchService.batchQueryServiceInfo(serviceIdList),
????????????branchPromise?=?this.branchService.batchQueryBranchInfo(branchIdList),
????????????res?=?await?Promise.all([servicePromise,?branchPromise]),
????????????[serviceInfos,?branchInfos]?=?res;
????????if?(serviceInfos.length?>?0)?{
????????????serviceInfos.forEach((service:?any)?=>?{
????????????????const?item:?ServiceBranchInfoListVO?=?{
????????????????????serviceId:?service.id,
????????????????????cmdbServiceId:?service.cmdbServiceId,
????????????????????cmdbServiceName:?service.cmdbServiceName,
????????????????????cmdbProductName:?service.cmdbProductName,
????????????????????branchList:?branchInfos.filter((branch:?any)?=>?branch.serviceId?===?service.id)
????????????????};
????????????????result.push(item);
????????????});
????????}
????????return?result;
????}
????...
2.4 Controller
根據(jù)具體的業(yè)務(wù)場景速勇,可以創(chuàng)建其他服務(wù)晌砾;
Controller層
controller是控制中心,所有的指令烦磁,調(diào)度都從這里發(fā)出去养匈。與service交互,只負(fù)責(zé)調(diào)用服務(wù)个初,不負(fù)責(zé)業(yè)務(wù)邏輯處理乖寒。
創(chuàng)建controller:BranchController
import?{?RestController,?RequestMapping,??PostMapping?}?from?'@tiger/boot';
import?{?Boom?}?from?'@tiger/error';
import?{?AppConfig,?RequestContext,?AjaxResult?}?from?'@tiger/core';
import?{?isNullOrUndefined?}?from?'util';
import?{?BranchManageService?}?from?'./branch-manage.service';
import?{??ServiceBranchInfoListVO,?BatchQueryBranchVO?}?from?'./vo/branch-info.vo';
@RestController
@RequestMapping(`${AppConfig.contextPath}${AppConfig.xhrPrefix}`,?[])
export?class?BranchController?{
????constructor(
????????private?service:?BranchManageService
????)?{?}
????/**?批量查詢分支信息?*/
????@PostMapping('/branch/queryBranch.json')
????async?queryBranch(ctx:?RequestContext<BatchQueryBranchVO,?AjaxResult<ServiceBranchInfoListVO[]>>)?{
????????//?參數(shù)校驗(yàn)
????????if?(isNullOrUndefined(ctx.request.body))?{
?????????????throw?Boom.notFound('參數(shù)錯(cuò)誤');
????????}
????????const?result?=?await?this.service.queryBranch(ctx.request.body.branchList);
????????ctx.body?=?AjaxResult.success(result);
????}
}
三、總結(jié)
這種分層結(jié)構(gòu)不僅僅是為了使代碼看上去清晰院溺,更像是我們對一個(gè)系統(tǒng)的拆解和組裝楣嘁,降低代碼的耦合度,提高代碼的可擴(kuò)展性珍逸。它可以讓你在遇到代碼交接的情況下減少提刀砍人的可能性逐虚,可以讓多人協(xié)作開發(fā)更容易。