美文网首页
Node项目不会代码分层怎么办?来看看网易大神怎么做

Node项目不会代码分层怎么办?来看看网易大神怎么做

作者: 金炳 | 来源:发表于2019-05-18 23:09 被阅读0次

    编者注:作者是网易高级前端@季含婷,当前负责网易供应商业务线前端开发以及接口管理平台建设工作,专注于中后台前端以及Node应用开发建设工作。

    Node项目不会代码分层怎么办?来看看网易大神怎么做

    一、背景

    刚刚接触Node开发或者后端开发的同学,有时候虽然能跟着网上的例子能去写出一个Node应用,但是网上的大神们或者项目领导说你对你的代码分层了吗?

    其实作为一个有追求的开发,自己也很想写出更健壮,更易维护性的大型项目,而不是网上demo级别的例子。

    那么我们应该怎么做呢?

    那这边就跟大家分享一下我们是如何通过代码分层,来提高项目的档次的。

    二、分层方案

    本文主要介绍项目中代码的分层结构,大致分为Dao、Model、Service、Controller层,这种分层模式在Java、Android和IOS都有被应用,下面我们看看在node中的应用。
    项目配置
    编程语言:NodeJS

    数据库:mysql

    第三方库:@tiger系列包+Sequelize

    项目目录结构

    2.1 Dao层

    一般Dao层是用来和底层数据库通信,负责对数据库的增删改查。

    在项目中用了第三方库Sequelize,它是一个基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, SQLite 和 Microsoft SQL Server. 它具有强大的事务支持, 关联关系, 读取和复制等功能.

    创建一个sequelize对象实例

    2.2 Model层

    Model即模型,常常和持久化的数据一一对应,Model承载的作用就是数据的抽象,描述了一个数据的定义,Model的实例就是一组组的数据。

    抽取model基类:BaseModel
    基类BaseModel包含一个model对象以及对应模型的CRUD操作,为继承它的子类提供数据库增删改查的操作方法

    import { sequelize } from '../dao';

    export class BaseModel {
        model: any;
        constructor(modelName: string, schema: any, option?: any) {
            this.model = sequelize.define(modelName, schema, option);
        }
        // 返回实例化的sequelize模型实例
        getModel() {
            return this.model;
        }
        // 多条查询
        findAll(option: any) {
            return this.model.findAll(option);
        }
        // 单条数据查询
        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);
        }
        // 插入单个实体
        create(entity: any) {
            return this.model.create(entity);
        }
        // 批量插入实体集
        createBatch(entitys: any) {
            return this.model.bulkCreate(entitys);
        }
    }

    创建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: '' },
                // 服务Id
                serviceId: { type: Sequelize.BIGINT, allowNull: false, defaultValue: 0 },
                // 分支描述
                description: { type: Sequelize.STRING(4096), allowNull: false, defaultValue: '' },
                // 分支创建人的邮箱
                createUser: { type: Sequelize.STRING(128), allowNull: false, defaultValue: '' },
                ...
            }, { 
                timestamps: false, 
                tableName: 'TB_YX_API_BRANCH' 
            });
            this.model = super.getModel();
            // 同步当前模型到数据库中
            this.model.sync();
        }
        // 特殊方法定义
        batchUpdate(){
            // ...
        }
    }

    2.3 Service层

    Service的重点是在于提供服务,可以处理事务和业务逻辑。

    创建BranchModel对应的service:BranchService
    BranchService主要提供BranchModel的数据处理服务;一个Model最好有一个与之对应的service,这个service包含了业务需要对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;
        }
    }

    创建controller对应的service:BranchManageService
    BranchManageService提供与controller对应的服务,主要做业务逻辑处理。一个controller最好有一个与之对应的service,这个service包含有controller调度的所有操作。

    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

    根据具体的业务场景,可以创建其他服务;
    Controller层
    controller是控制中心,所有的指令,调度都从这里发出去。与service交互,只负责调用服务,不负责业务逻辑处理。

    创建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[]>>) {
            // 参数校验
            if (isNullOrUndefined(ctx.request.body)) {
                 throw Boom.notFound('参数错误');
            }
            const result = await this.service.queryBranch(ctx.request.body.branchList);
            ctx.body = AjaxResult.success(result);
        }
    }

    三、总结

    这种分层结构不仅仅是为了使代码看上去清晰,更像是我们对一个系统的拆解和组装,降低代码的耦合度,提高代码的可扩展性。它可以让你在遇到代码交接的情况下减少提刀砍人的可能性,可以让多人协作开发更容易。

    相关文章

      网友评论

          本文标题:Node项目不会代码分层怎么办?来看看网易大神怎么做

          本文链接:https://www.haomeiwen.com/subject/jmixzqtx.html