Node項(xiàng)目不會(huì)代碼分層怎么辦棺妓?來看看網(wǎng)易大神怎么做

編者注:作者是網(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ā)更容易。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谆膳,一起剝皮案震驚了整個(gè)濱河市叭爱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌漱病,老刑警劉巖买雾,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件把曼,死亡現(xiàn)場離奇詭異,居然都是意外死亡漓穿,警方通過查閱死者的電腦和手機(jī)嗤军,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晃危,“玉大人叙赚,你說我怎么就攤上這事×欧梗” “怎么了震叮?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鳍鸵。 經(jīng)常有香客問我苇瓣,道長,這世上最難降的妖魔是什么权纤? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任钓简,我火速辦了婚禮,結(jié)果婚禮上汹想,老公的妹妹穿的比我還像新娘。我一直安慰自己撤蚊,他們只是感情好古掏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侦啸,像睡著了一般槽唾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上光涂,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天庞萍,我揣著相機(jī)與錄音,去河邊找鬼忘闻。 笑死钝计,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的齐佳。 我是一名探鬼主播私恬,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼炼吴!你這毒婦竟也來了本鸣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤硅蹦,失蹤者是張志新(化名)和其女友劉穎荣德,沒想到半個(gè)月后闷煤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涮瞻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年曹傀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饲宛。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡皆愉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出艇抠,到底是詐尸還是另有隱情幕庐,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布家淤,位于F島的核電站异剥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏絮重。R本人自食惡果不足惜冤寿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望青伤。 院中可真熱鬧督怜,春花似錦、人聲如沸狠角。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丰歌。三九已至姨蟋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間立帖,已是汗流浹背眼溶。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晓勇,地道東北人堂飞。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像宵蕉,于是被迫代替她去往敵國和親酝静。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容