依稀记得我第一次设计一个系统的时候,画了一堆UML图,面对Class Diagram(其实就是领域模型),纠结了好久,不知道如何落地。因为,如果按照这个类图去落数据库的话,看起来很奇怪,有点繁琐。可是不按照这个类图落库的话,又不知道这个类图画了有什么用。
现在回想起来,我当时的纠结源自于我对领域模型和数据模型这两个重要概念的不清楚。最近,我发现对这两个概念的混淆不是个例,而是非常普遍的现象。其结果就是,小到会影响一些模块设计的不合理性,大到会影响像业务中台这样重大技术决策,因为如果底层的逻辑、概念、理论基础没搞清楚的话,其构建在其上的系统也会出现问题,非常严重的问题。
鉴于很少看到有人对这个话题进行比较深入的研究和探讨,我觉得有必要花时间认真明晰这两个概念,帮助大家在工作中,更好的做设计决策。
领域模型关注的是领域知识,是业务领域的核心实体,体现了问题域里面的关键概念,以及概念之间的联系。领域模型建模的关键是看模型能否显性化、清晰的表达业务语义,扩展性是其次。
数据模型关注的是数据存储,所有的业务都离不开数据,都离不开对数据的CRUD,数据模型建模的决策因素主要是扩展性、性能等非功能属性,无需过分考虑业务语义的表征能力。
按照Robert在《整洁架构》里面的观点,领域模型是核心,数据模型是技术细节。然而现实情况是,二者都很重要。
这两个模型之所以容易被混淆,是因为两者都强调实体(Entity),都强调关系(Relationship),这可不,我们传统的数据库的数据模型建模就是用的ER图啊。
是的,二者的确有一些共同点,有时候领域模型和数据模型会长的很像,甚至会趋同,这很正常。但更多的时候,二者是有区别的。正确的做法应该是有意识的把这两个模型区别开来,分别设计,因为他们建模的目标会有所不同。如下图所示,数据模型负责的是数据存储,其要义是扩展性、灵活性、性能。而领域模型负责业务逻辑的实现,其要义是业务语义显性化的表达,以及充分利用OO的特性增加代码的业务表征能力。
然而,现实情况是,我们很多的业务系统设计,并没有很好的区分二者的关系。经常会犯两个错误,一个是把领域模型当数据模型,另一个是把数据模型当领域模型。
这几天我在做一个报价优化的项目,里面涉及到报价规则的问题,这块的业务逻辑大意是说,对于不同的商品(通过类目、品牌、供应商类型等维度区分),我们会给出不同的价格区间,然后来判断商家的报价是否应该被自动审核(autoApprove)通过,还是应该被自动拦截(autoBlock)
对于这个规则,领域模型很简单,就是提供了价格管控需要的配置数据,如下图所示:
如果按照这个领域模型去设计我们的存储的话,自然是需要两张表:price_rule和price_range,一张用来存价格规则,一张是用来存价格区间。
如果这样去设计数据模型,我们就犯了把领域模型当数据模型的错误。这里,更合适的做法是一张表就够了,把price_range作为一个字段在price_rule中用一个字段存储,如下图所示,里面的多个价格区间信息用一个json字段去存取就好了。
这样做的好处很明显:
可是,在业务代码里面,如果是基于json在做事情可不那么美好。我们需要把json的数据对象,转换成有业务语义的领域对象,这样,我们既可以享受数据模型扩展性带来的便捷性,又不失领域模型对业务语义显性化带来的代码可读性。
的确,数据模型最好尽量可扩展,毕竟,改动数据库可是个大工程,不管是加字段、减字段,还是加表、删表,都涉及到不少的工作量。
说到数据模型的扩展设计经典之作,非阿里的业务中台莫属,核心的商品、订单、支付、物流4张表,得益于良好的扩展性设计,就支撑了阿里几十个业务的成千上万的业务场景。
拿商品中台来说,它用一张auction_extend垂直表,就解决了所有业务商品数据存储扩展性的需求。理论上来说,这种数据模型可以满足无限的业务扩展。
JSON字段也好,垂直表也好,虽然可以很好的解决数据存储扩展的问题。但是,我们最好不要把这些扩展(features)当成领域对象来处理,否则,你的代码根本就不是在面向对象编程,而是在面向扩展字段(features)编程,从而犯了把数据模型当领域模型的错误。更好的做法,应该是把数据对象(Data Object)转换成领域对象来处理。
如下所示,这种代码里面到处是getFeature、addFeature的写法,是一种典型的把数据模型当领域模型的错误示范。
上面展示的代码,是一个在某中台上写业务代码的同学,在离职那天发给我看的,他说他受够了这种乱七八糟的代码,但作为一个底层小P,又无能改变局面,无奈之下,只能选择离开。
上面展示了因为混淆领域模型和数据模型,带来的问题。正确的做法应该是把领域模型、数据模型区别开来,让他们各司其职,从而更合理的架构我们的应用系统。
其中,领域模型是面向领域对象的,要尽量具体,尽量语明确,显性化的表达业务语义是其首要任务,扩展性是其次。而数据模型是面向数据存储的,要尽量可扩展。
在具体落地的时候,我们可以采用COLA的架构思想,使用gateway作为数据对象(Data Object)和领域对象(Entity)之间的转义网关,其中,gateway除了转义的作用,还起到了防腐解耦的作用,解除了业务代码对底层数据(DO、DTO等)的直接依赖,从而提升系统的可维护性。
此外,教科书上教导我们在做关系数据库设计的时候,要满足3NF(三范式),然而,在实际工作中,我们经常会因为性能、扩展性的原因故意打破这个原则,比如我们会通过数据冗余提升访问性能,我们会通过元数据、垂直表、扩展字段提升表的扩展性。
业务场景不一样,对数据扩展的诉求也不一样,像price_rule这种简单的配置数据扩展,json就能胜任。复杂一点的,像auction_extend这种垂直表也是不错的选择。
wait,有同学说,你这样做,数据是可扩展了,可数据查询怎么解决呢?总不能用join表,或者用like吧,实际上,对一些配置类的数据,或者数据量不大的数据,完全可以like。然而,对于像阿里商品、交易这样的海量数据,当然不能like,不过这个问题,很容易通过读写分离,构建search的办法解决。
最后,再给一个思考题吧。
前面提到的数据扩展,还都是领域内的有限扩展。如果我连业务领域是什么还不知道,能不能做数据扩展呢? 可以的,salesforce的force.com就是这么做的,其底层数据存储完全是元数据驱动的(metadata-driven),他用一张有500个匿名字段的表,去支撑所有的SaaS业务,每个字段的实际表意是通过元数据去描述的。如下图所示,value0到value500都是预留的业务字段,具体代表什么意思,由metadata去定义。
说实话,这种实现方式的确是一个很有想法,很大胆的设计,也的确支撑了上面数以千计的SaaS应用和salesforce千亿美金的市值。
只是,我不清楚从元数据到领域对象的映射,salesforce具体是怎么做的,是通过他们的语法糖apex?如果没有领域对象,他们的业务代码要怎么写呢?反正据在salesforce里面做vendor的同学说,他们所谓的low code,里面还是有很多用apex写的代码,而且可维护性一般。
anyway,我们绝大部分的应用都是面向确定问题域的,不需要像Salesforce那样提供“无边际”的扩展能力。在这种情况下,我认为,领域对象是最好的连接数据模型和业务逻辑的桥梁。
驱动代码位于: sound/soc/codec/alc5623.c随便找个Linux内核都会有。1、首先进行i2c总线驱动加载在:static int __init alc5623_modinit(void)在该函数中:i2c_add_driver(&alc5623_i2c_driver);alc5623_i2c_driver是一个结构体变量,并且已经被初始化,我们来看看它做了什么?sta...
一、表格1.table里的属性table{ bordr-collapse:collapse(合并)|separate(不合并) /*不合并边框的时候,可以让空的单元格隐藏*/ /*empty-cells: hide;*/ //单元格里的文字左右居中 text-align: center;}2....
Ubantu下安装OracleJDK由于OpenJDK有些功能和OracleJDK不一样,有些开发必须要用到OracleJDK,但OracleJDK的安装较OpenJDK比较麻烦,网上教程各种不一,因此我记录下我的安装步骤,供大家参考。具体步骤只有3步:1、下载OracleJDK。2、解压至本地,并配置文件,告诉系统OracleJDK在哪。3、使配置生效。详细操作1、登陆Oracle网站,下载你需要的JDK包https://www.oracle.com/cn/java/technologie
一、Ping命令ping是个使用频率极高的实用程序,主要用于确定网络的连通性。这对确定网络是否正确连接,以及网络连接的状况十分有用。简单的说,ping就是一个测试程序,如果ping运行正确,大体上就可以排除网络访问层、网卡、Modem的输入输出线路、电缆和路由器等存在的故障,从而缩小问题的范围。ping能够以毫秒为单位显示发送请求到返回应答之间的时间量。如果应答时间短,表示数据报不必通过太多的路由器或网络,连接速度比较快。(1) 命令格式ping 主机名; ping 域名; ping IP地址。这
自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能。开启自定义菜单后,公众号界面如图所示: http请求方式:POST(请使用https协议)https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN请求示例{ "button":[ { ...
KARMARKAR标准型转换与迭代算法实现karmarkar标准型转换karmarkar迭代过程main函数karmarkar标准型转换标准型转换的原理就不说了,参见高等数学规划教材。def trans_to_karmarkar(a, b, c, m, n): # 约束矩阵A 等式右端b(列向量) 目标参数c(列向量) 约束数量m 变量数量n at = np.transp...
pycharm 报错:pycharm please specify a different SDK name我在给项目配虚拟环境里的解释器的时候有没有遇到过这个问题的啊,就是一个正常的项目,解释器忽然丢了,解释器是配在虚拟环境里面的,再去选择解释器就一直报这个错,给现有项目添加虚拟环境的时候也是报这个错——pycharm please specify a different SDK name...
首先介绍一下什么叫做富文本后台常常要用到一个编辑器叫做富文本编辑器,例如淘宝和电商等等的详情页面编辑等等,这个编辑器可以参考http://ueditor.baidu.com/website/onlinedemo.html它编辑出来的文本我们可以看到是一个这样的一段内容,如果在前端显示这个就太容易了div v-html=
前提:拿下边界机之后,进入内网,想用nmap怎么办? CS可以开启代理,但是是socks4的代理,只能使用tcp协议,所以nmap使用的时候要使用-sT选择使用tcp_协议,要使用-Pn不使用ICMP_的ping确认主机存活...
【群主】翁老师的官方网站 11:01:5712年程序员大牛竟然离开北上广?想定居二线城市!【群主】翁老师的官方网站 11:02:05你们可以看一下,这个老程序员的。【群主】翁老师的官方网站 11:02:16他说的都不错,只是他不懂得找赛道。【总监】深圳-已失业-被炒了 11:03:00上辈子投错胎【群主】翁老师的官方网站 11:03:54不是说要买房,而是他缺少一个群,或者一个...
介绍:分享一个在外头扒HTML的单页源码。熟话说的好美丽的身体经不住诱惑此单页既简单有实用所以就巴拉下来了,本地csso网盘下载地址:http://kekewangLuo.net/zDD6rO6UHkE0图片:
————————————- read the fucking source code——————————– Android开发中经常会遇到一些没处理过的错误,一时也不知道怎么处理,后来通过百度才知其所以然。为了方便在Android开发过程中提高错误处理效率,特在这篇文章中汇总错误的现象及处理的办法。欢迎评论补充修正。1.Error:Unrecognized Windows Socke...