数据库schema不是CRUD服务的一切
来源:SegmentFault
时间:2023-01-21 16:25:01 250浏览 收藏
来到golang学习网的大家,相信都是编程学习爱好者,希望在这里学习数据库相关编程知识。下面本篇文章就来带大家聊聊《数据库schema不是CRUD服务的一切》,介绍一下MySQL、架构、后端、databaes、architecture,希望对大家的知识积累有所帮助,助力实战开发!
谨以本文向我脑海中那些不成熟的想法致敬。
序言
受疫情影响呆在家中的这段时间里,我收尾了《Clean Architecture》。这本书给了我许多新知识和启发,包括本文的中心论点——数据库schema不是CRUD服务的一切,也是在读书过程中想到的。在书中,作者的原话是
But the database is not the data model
它出现在书中第六部分《Details》的第一个章节中。作者认为,从架构的角度来看,数据库不是一个实体而是一个细节,不足以成为架构中的一个元素。他甚至打了个比方:数据库对于架构而言,就好像门把手对于房子一般。并且,作者进一步澄清了他的观点:他口中所说的数据库,不是指的数据模型。应用内的数据结构对架构而言至关重要,但数据库并不是数据模型。
这不禁让我回忆起了自己早期写设计文档的套路。
在我的从业生涯早期(说得好像我从业很久了一样),每当需要开发一个新的Web服务时,必须先写一份简要的设计文档,向上级清楚地表达我的实现思路,包括:
- 如何与其它服务协作完成产品提出的需求;
- 服务的接口描述;
- 数据的存储结构;
- 关键的算法等。
那时候的我会先考虑数据的存储结构,然后定义接口,最后才是与其它服务的协作。这些早期设计文档的其中一个特点是:接口的响应格式,与数据的存储结构是相同的。
比方说我要设计一个网上商城的订单服务,可能会提供如下查询特定订单的接口
{ order: { id: 'F122663A-A5DC-451A-9B79-92DCE2EE41F1', price: '100.00', products: [ { id: 1, name: 'MacBook Pro', price: '19999.00' }, { id: 2, name: 'iPhone', price: '6999.00' } ] } }
为了保存这种“不平坦”的对象,将会用MongoDB作为存储——除了文档的主键
_id之外,其它字段在接口和存储之间一一对应。
不仅仅是响应格式,在这个Web服务内所操作的也是同样结构的对象。用MongoDB Node.JS Driver获得的订单恰好是一个JS对象,它与collection的文档有着一模一样的结构。之后这些对象会在代码内到处流通,不加修饰地使用。定义了数据库schema(就算是用MongoDB也有一套脑内的schema)后,其它的一切也就跟着确定了。
业界甚至有工具可以直接从数据库得到API,比如postgrest。
很多时候,数据库schema成了一个应用内事实上的数据模型。但是,即便它们可以偶然一样,也不要认为它们总应该一样。
适合存储,不一定适合计算
以MySQL为例,在
CREATE TABLE语句中某一列的类型实际上决定的是存储时分配的空间的多少。但适合存储的类型,并不一定也适合业务逻辑的运算。
比如说,要在MySQL中存储“开关型”的数据,即诸如“是否启用”或“是否已支付”这样非此即彼的状态时,通常定义为
TINYINT类型,用0表示逻辑假(“未启用”和“未支付”),1表示逻辑真(“已启用”和“已支付”)。但对代码而言,比起用数值类型,布尔类型才是更恰当的选择。尤其是当所选择的语言并没有将0与
false、1与
true等价起来的时候——在Common Lisp中,
(if 0 1 2)的求值结果为1。
适合计算,不一定适合存储
通常数据结构在内存中比在磁盘上要容易表达得多,所以代码中使用的数据结构会比数据库中存储的要灵活不少,这同样造成了两者的不匹配。
以我自己开发的提醒工具cuckoo
为例,应用内有两种对象:任务和提醒。任务描述了要做的事情,提醒描述了在什么时候该告诉用户。显然,提醒是一个依赖于任务的弱实体。在cuckoo的代码中,任务是
Task类的实例对象,有一个名为
remind的成员变量存储着提醒。
但这样的结构不方便存储在MySQL中。遵照关系型数据库设计的第一范式,任务和提醒分别被存储在
t_task和
t_remind表中,两者通过
t_task.remind_id联系起来。
当然,也可以在一开始就用MongoDB来存储这些数据(甚至可以用对象数据库?不过我没玩过)。尤其是cuckoo只是一个小玩意儿,MySQL和MongoDB都足以胜任。但作为一名有理想的程序员,在做设计的时候,不应该让低层细节过分干预高层策略。(在《Clean Architecture》中,越是接近I/O的越是low-level,反之则是high-level。)
面向业务逻辑,而非存储结构
业务逻辑和规则才是一个服务的核心,应该把更多精力花在实现业务逻辑的数据结构和算法上。
以网上商城中常见的优惠券功能为例。优惠券服务所管理的优惠券往往有着各种效果、条件,以及限制。为了保持灵活性,优惠券类(下称
Coupon)的实例对象中会有三种接口类型的成员变量:
-
Effect
类型的变量effect
,负责实现优惠效果的计算逻辑; -
Condition
数组类型的变量conditions
,负责实现使用条件的检查逻辑; -
Restriction
数组类型的变量restrictions
,负责实现使用限制的检查逻辑。
三个接口可以有各种各样的实现——定额减免、折扣减免、某年月日前可用、不可用于电子产品,等等。如此,优惠券功能具备了极大的灵活性,业务可以随心所欲,产品可以为所欲为,老板数钱数到手软,公司业绩蒸蒸日上。
那么如何存储
Effect、
Condition、
Restriction、
Coupon类的实例对象呢?没有唯一的选择,既可以存储在MySQL中,也可以存储在MongoDB中,或者别的什么数据库中。不管这些数据最终如何持久化,都不会影响作为高层策略的优惠券业务逻辑。反过来,如果在代码中处理的不是类、接口,以及实例对象,而是直接从数据库中取出来的、贫血模型的行(或文档),处理起来就不是很优雅了——可以预见 ,代码中会充斥着许多的
if-else判断逻辑。
数据库只是帮忙从磁盘中读取数据的软件,它的schema不应该直接成为应用的数据模型。
Interface Segregation Principle
不应该在HTTP接口的响应中直接暴露数据库的schema。
不说别的,光是数据库schema与接口规格所使用的命名规则就足以造成差异了。也许在MySQL中用snake case
命名一列,却又在HTTP响应的JSON对象中用camel case
命名字段。
此外,除非这些接口仅仅实现增删查改、没有任何的业务逻辑或规则,否则一个服务更应当提供与业务需求恰好契合的接口。仍然以上文的优惠券服务为例,尽管内部可能
Effect、
Condition、
Restriction、
Coupon等诸多概念,但
如果优惠券服务直接将数据库中的行(或文档)序列化成JSON返回给调用者,会导致封装的泄露。每一个查询优惠券的调用方,都必须了解优惠券的内部表示形式,必须知道效果由
effect描述、用券后的订单金额是多少、
conditions中有关于过期与否的信息,等等。每增加一个优惠券服务的使用者,就相应地增加一套描述这些内容的代码。甚至当优惠券服务自身重构的时候,也许牵连到众多的调用方。
如果直接将存储结构暴露给调用者的话,又何必再做一个Web服务呢。
切勿矫枉过正
的确存在这样的例子,数据库schema、数据模型,以及HTTP响应结构三者相同。这是因为比起维护数据库schema与数据模型的转换规则,以及DTO与数据模型的转换规则而言,在领域代码中直接使用数据库schema来表达数据模型的成本更低一点。尽管数据库schema不是Web服务的一切,但很多时候可以因地制宜地妥协一下。
以上就是《数据库schema不是CRUD服务的一切》的详细内容,更多关于mysql的资料请关注golang学习网公众号!
-
499 收藏
-
286 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
335 收藏
-
467 收藏
-
303 收藏
-
176 收藏
-
368 收藏
-
475 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 端庄的眼神
- 真优秀,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢作者大大分享文章内容!
- 2023-06-02 04:45:32
-
- 迷人的大叔
- 这篇博文真及时,细节满满,赞 👍👍,码住,关注大佬了!希望大佬能多写数据库相关的文章。
- 2023-04-20 10:54:51
-
- 帅气的小土豆
- 这篇文章真是及时雨啊,太全面了,很好,已收藏,关注博主了!希望博主能多写数据库相关的文章。
- 2023-03-22 16:24:03
-
- 甜蜜的羊
- 细节满满,收藏了,感谢作者的这篇技术贴,我会继续支持!
- 2023-02-02 02:27:15
-
- 时尚的仙人掌
- 这篇文章内容出现的刚刚好,很详细,受益颇多,码住,关注作者大大了!希望作者大大能多写数据库相关的文章。
- 2023-02-01 08:30:39
-
- 自觉的大神
- 这篇技术贴真及时,作者大大加油!
- 2023-01-31 03:38:32
-
- 淡然的蜜粉
- 受益颇多,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢作者大大分享文章内容!
- 2023-01-31 00:59:02
-
- 如意的蜜粉
- 这篇博文出现的刚刚好,很详细,真优秀,mark,关注作者大大了!希望作者大大能多写数据库相关的文章。
- 2023-01-27 17:45:51
-
- 稳重的鸡翅
- 太全面了,收藏了,感谢博主的这篇技术贴,我会继续支持!
- 2023-01-23 13:48:29