编写高质量可维护的代码:数据建模

这是第 67 篇不掺水的原创,想获取更多原创好文,请搜索公众号关注咱们吧~ 本文首发于政采云前端博客:编写高质量可维护的代码:逻辑判断javascript

什么是数据建模

数据建模是一种用于定义和分析数据的要求和其须要的相应支持的信息系统的过程。前端

随着前端页面的交互变得更加细腻复杂,本来存放于服务端的状态放置在了前端,相似 flux、redux、mobx、dva、rematch、vuex 的状态管理库也成了每一个项目的标配。vue

由于分层理念的普及,前端工程师们须要把更多精力放在数据管理上,数据建模也成了基本功。java

而建模的产物是数据模型,数据模型是定义数据如何输入和输出的一种模型,其主要做用是为信息系统提供数据的定义和格式。vuex

数据模型包括数据结构、数据操做、数据完整性约束条件这三要素。redux

简单理解就是数据模型提供了一个“模具”,数据按照预先的设计和约束进行放置。后端

三要素

数据完整性约束条件

好的数据结构必需要有约束,例如描述同一个状态的字段有时候是字符串,有时候是数字,这样的话就容易形成预期以外的状况。添加约束能够最大限度保障这份数据是干净整齐的;api

// status 是字符串的时候不经过
if (status === 1) {
  ...
}
复制代码
// 按照必定约束
model.define(
  'user',
  {
    name: { 
      field: 'name',
      type: STRING(64),
      allowNull: false, 
      comment: '姓名',
	},
    sex: {
	  field: 'sex',
      type: INTEGER(1),
      allowNull: false,
      comment: '性别',
    }
  }
);
复制代码

数据结构

描述模型自己的性质以外,还需经过某些字段表达模型(表)和模型之间的关联;性能优化

数据操做

在数据结构上对数据或者数据之间的关联关系的操做。markdown

领域驱动设计

在围绕着数据模型进行应用开发的时候,咱们会思考如何进行建模呢?

实际上,软件开发行业中已经积累了一些方法论,例如**领域驱动设计(DDD)**就被普遍采用。

在进行软件开发前,一般须要先进行业务知识梳理,然后到达软件设计的层面,最后才是开发。而在业务知识梳理的过程当中,咱们必然会造成某个领域知识。根据领域知识来一步步驱动软件设计,就是领域驱动设计的基本概念。简单来讲领域驱动设计就是关注精简的业务模型及实现的匹配

分层架构

按照领域驱动设计的分层架构能够将应用进行分层

  • UI 层:负责向用户展示信息以及解释用户命令。
  • 应用层:用来协调应用的活动。它不包含业务逻辑;它不保留业务对象的状态;但它保有应用任务的进度状态。
  • 领域层:业务软件的核心所在。在这里保留业务对象的状态,对业务对象和它们状态的持久化被委托给了基础设施层。
  • 基础设施层:为其余层的支撑库存在。它提供了层间的通讯,实现对业务对象的持久化,包含对用户界面层的支撑库等做用。

按照这个分层,越往左边代码变更越频繁。随着业务复杂,应用层和领域层的边界变得模糊,领域之间也容易交错在一块儿。

良好的设计应该避免层与层之间产生过多依赖,若是代码没有被清晰地隔离到某层中,它会迅即变得混乱和难以维护。

经过分层架构和高内聚低耦合的设计思想,最终实现系统与需求有较好的一致性,在业务迭代中快速响应需求变动。

实体

实体在领域模型中是必需的对象,而且它们应该在建模过程开始时就被考虑。例如要实现一个“猫”的概念,咱们可能会去创造一个 Cat 的类,这个 Cat 可能包含名称、性别、品种等属性,可是这些属性都不足以区分这只猫,因此咱们须要建立一个惟一不重复的 ID 来区分他们,也就区分实体的标识符。

建立 ID 的方式有不少种,它能够是主键、能够来自外部、也能够由系统本身产生,但它必须符合模型中的身份差异。

值对象

用来描述领域的特殊方面,且没有标识符的一个对象,叫作值对象。例如画布上的一个点 Customer 会跟姓名、省份、城市、区、街道相关。最好是将地址分离出来,保留对地址的引用,由于它们都是同一个址属性。

服务

你能够简单地将行为理解成一种服务。例如你去商店购买商品,你的朋友也能够去购买商品。若是将购买这个能力做为一个属性放在 Person 这个实体里显然有点不对劲,由于“去购买”这个功能并不属于你和你的朋友(实体或者值对象),同时去购买也可能涉及到商品对象。

保证服务的单一性和隔离很是重要,注意区分领域服务和应用服务。决定一个服务所应归属的层是很是困难的事情,咱们在设计阶段创建模型时,须要确保领域层从其余层中隔离开来。

模块

模块是一种被用来做为组织相关概念和任务以便下降复杂性的方法,一般状况下由功能或者逻辑上属于一体的元素构成,以保证高内聚,同时经过接口的形式暴露给第三方以下降模块之间的耦合。

聚合

聚合是针对数据变化能够考虑成一个单元的一组相关对象。聚合基于(有且仅有)一个实体(根),聚合经过这个根被外部访问,它能够引用任意聚合或者被其余聚合引用。如下是一个简单的聚合例子:客户做为聚合的根,其余信息都是客户内部的,若是须要地址则将地址的拷贝传递出去( Javascript 中特别须要注意)。

工厂

工厂用来封装对象建立所必需的知识,它们对建立聚合特别有用。工厂方法是一个对象的方法,包含并隐藏了建立其余对象的必要知识。

资源库

资源库做为一个全局可访问对象的存储点而存在。它是一个独立的层,介于领域层与数据映射层(数据访问层)之间。它的存在让领域层感受不到数据访问层的存在,它提供一个相似集合的接口,提供给领域层进行领域对象的访问。

前端的数据建模

数据建模和后端的工做关联较为紧密,前端的数据模型更可能是依赖后端传递的数据传输对象(DTO)进行二次构建。不管二次构建是发生在服务端聚合阶段仍是用户端 AJAX 请求完成阶段,前端都须要参与必定的数据清洗,并应用到前端的数据模型之上。

领域划分

如今你能够开始尝试划分你应用内的业务领域。以一个商城为例子,它可能会包括用户、商品、货架、订单、结算、帐户等内容。

每个业务领域均可以致少拆分红一个领域,按照业务领域来组织代码,例如在交易领域中按照如下目录结构划分:

src
  modules 
    ...
    trading             # 交易领域
      components/         # 组件
      models/             # models
      pages/              # 页面
      redux/              # redux
      services/           # 交易模块相关api
      styles/             # 交易模块样式
      index.ts
  ...
复制代码

概念模型

数据建模的前提是对业务的充分理解,充分理解业务至关于在更高的视角去看待业务之间的关系,有利于更好地完成模型建设。

尝试回想一下你所维护的业务(应用)场景,你是否清晰业务场景和业务对象之间的关系以及具体交互?

使用思惟导图梳理出概念模型,这个阶段能够不用严格遵照三要素,目标清晰表达现实世界就行。

定义模型

定义模型能够依据概念模型,补充细节和关联关系,例如简单定义一个营销商品:

以上展现了商场货架上划分的一块活动区域,规则是满 XX 减 XX ,再将参与该活动的商品在区域内进行上架。

下降复杂度

在大部分状况下,特别是展现逻辑这块,前端不该该是重逻辑的。

以商品为例,不一样商品的营销类型背后隐藏着复杂的价格体系,尽管是同一种营销类型,商品在不一样的状态展现的价格也不必定相同。你能够想象这背后的字段,以及计算规则。

假如后端把这些字段、各类price和规则一股脑抛给你,先不谈先后端对称问题,光挑字段都能让你目瞪狗呆。

遇到相似状况更好的办法是:尽可能避免在前端(用户端)去处理复杂的业务判断,在聚合层或者让后端同窗给你处理好这些展现逻辑。

特别是在 C 端场景下,数据直出显得更加剧要,同时前端同窗也有更多时间去作性能优化(早点下班不香么?)。

另一个好处是假如出现展现问题,你只要肯定读取的字段正确,剩下的仅需一我的排查就够了;

// Bad
const switchPrice = product => {
  switch(product.status) {
    case 0:
    	return product.priceA;
    case 1:
    	return product.priceB;
    case 2:
    	return product.priceB;
    default:
    	return  product.priceBase;
  }
}
<Price value={switchPrice(product)}/>
     
// Good
<Price value={product.price}/>
复制代码

逻辑分层

设计上须要区分应用逻辑(业务逻辑)和展现逻辑。应用层注重对领域层的调度,是业务逻辑的实现,展现层专一渲染和交互动做。

在一个大型项目中,同一个 Model 可能被多处引用,你很难肯定谁最终会对同一份数据进行怎样的操做。

同时 Model 中仅保留数据源的抽象结构,而不修改数据源的内容。

// 在视图层只作展现逻辑处理
// 组件A
...
<>
	<span>日期:{format(res.date, 'YYYY-MM-DD')}</span>
</>

// 组件B
...
<>
	<span>日期:{format(res.date, 'YYYY-MM')}</span>
</>
复制代码

统一字段

在设计模型的时候,尽量与后端保持统一字段。好比某些表单场景在回显和提交的时候要多一层转换,后期维护会带来多一层心智负担。在先后端分离的开发模式下,不必定能保证后端会先给出字段,个人习惯是标记字段,等联调的时候全局替换一下就好了。

简化字段、明确语义、改变不合理的先后端交互是作好数据建模的基础,不然你将花费大量时间去理解这些字段背后的含义和计算规则。

小结

没有一个十全十美的数据模型能够适用任何需求场景,模型的落地须要综合考虑业务实际场景和技术选型。在构建模型的过程当中,锻炼系统性思考能力、从更高的视角看待业务,才能创造出一个生命周期更长的模型。

推荐阅读

编写高质量可维护的代码:优化逻辑判断

前端文档站点搭建方案

招贤纳士

政采云前端团队(ZooTeam),一个年轻富有激情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 50 余个前端小伙伴,平均年龄 27 岁,近 3 成是全栈工程师,妥妥的青年风暴团。成员构成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在平常的业务对接以外,还在物料体系、工程平台、搭建平台、性能体验、云端应用、数据分析及可视化等方向进行技术探索和实战,推进并落地了一系列的内部技术产品,持续探索前端技术体系的新边界。

若是你想改变一直被事折腾,但愿开始能折腾事;若是你想改变一直被告诫须要多些想法,却无从破局;若是你想改变你有能力去作成那个结果,却不须要你;若是你想改变你想作成的事须要一个团队去支撑,但没你带人的位置;若是你想改变既定的节奏,将会是“5 年工做时间 3 年工做经验”;若是你想改变原本悟性不错,但老是有那一层窗户纸的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但愿参与到随着业务腾飞的过程,亲手推进一个有着深刻的业务理解、完善的技术体系、技术创造价值、影响力外溢的前端团队的成长历程,我以为咱们该聊聊。任什么时候间,等着你写点什么,发给 ZooTeam@cai-inc.com